Skip to content

Add Adapter for RPC client #152

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 6 additions & 12 deletions lib/ethers.ex
Original file line number Diff line number Diff line change
Expand Up @@ -459,8 +459,8 @@ defmodule Ethers do
- `:address`: Indicates event emitter contract address. (nil means all contracts)
- `:rpc_client`: The RPC Client to use. It should implement ethereum jsonRPC API. default: Ethereumex.HttpClient
- `:rpc_opts`: Extra options to pass to rpc_client. (Like timeout, Server URL, etc.)
- `:fromBlock`: Minimum block number of logs to filter.
- `:toBlock`: Maximum block number of logs to filter.
- `:fromBlock` | `:from_block`: Minimum block number of logs to filter.
- `:toBlock` | `:to_block`: Maximum block number of logs to filter.
"""
@spec get_logs(map(), Keyword.t()) :: {:ok, [Event.t()]} | {:error, atom()}
def get_logs(event_filter, overrides \\ []) do
Expand Down Expand Up @@ -564,19 +564,11 @@ defmodule Ethers do

@doc false
@spec rpc_client() :: atom()
def rpc_client, do: Application.get_env(:ethers, :rpc_client, Ethereumex.HttpClient)
defdelegate rpc_client(), to: Ethers.RpcClient

@doc false
@spec get_rpc_client(Keyword.t()) :: {atom(), Keyword.t()}
def get_rpc_client(opts) do
module =
case Keyword.fetch(opts, :rpc_client) do
{:ok, module} when is_atom(module) -> module
:error -> Ethers.rpc_client()
end

{module, Keyword.get(opts, :rpc_opts, [])}
end
defdelegate get_rpc_client(opts), to: Ethers.RpcClient

defp pre_process(tx_data, overrides, :call = _action, _opts) do
{block, overrides} = Keyword.pop(overrides, :block, "latest")
Expand Down Expand Up @@ -658,7 +650,9 @@ defmodule Ethers do
event_filter
|> EventFilter.to_map(overrides)
|> ensure_hex_value(:fromBlock)
|> ensure_hex_value(:from_block)
|> ensure_hex_value(:toBlock)
|> ensure_hex_value(:to_block)

{:ok, log_params}
end
Expand Down
23 changes: 23 additions & 0 deletions lib/ethers/rpc_client.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
defmodule Ethers.RpcClient do
@doc false
@spec rpc_client() :: atom()
def rpc_client do
case Application.get_env(:ethers, :rpc_client, Ethereumex.HttpClient) do
Ethereumex.HttpClient -> Ethers.RpcClient.EthereumexHttpClient
module when is_atom(module) -> module
_ -> raise ArgumentError, "Invalid ethers configuration. :rpc_client must be a module"
end
end

@doc false
@spec get_rpc_client(Keyword.t()) :: {atom(), Keyword.t()}
def get_rpc_client(opts) do
module =
case Keyword.fetch(opts, :rpc_client) do
{:ok, module} when is_atom(module) -> module
:error -> Ethers.rpc_client()
end

{module, Keyword.get(opts, :rpc_opts, [])}
end
end
26 changes: 26 additions & 0 deletions lib/ethers/rpc_client/adapter.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
defmodule Ethers.RpcClient.Adapter do
@type error :: {:error, map() | binary() | atom()}

@callback batch_request([{atom(), list(boolean() | binary())}], keyword()) ::
{:ok, [any()]} | error

@callback eth_block_number(keyword()) :: {:ok, binary()} | error()

@callback eth_call(map(), binary(), keyword()) :: {:ok, binary()} | error()

@callback eth_estimate_gas(map(), keyword()) :: {:ok, binary()} | error()

@callback eth_gas_price(keyword()) :: {:ok, binary()} | error()

@callback eth_get_balance(binary(), binary(), keyword()) :: {:ok, binary()} | error()

@callback eth_get_transaction_by_hash(binary(), keyword()) :: {:ok, map()} | error()

@callback eth_get_transaction_count(binary(), binary(), keyword()) :: {:ok, binary()} | error()

@callback eth_get_transaction_receipt(binary(), keyword()) :: {:ok, map()} | error()

@callback eth_max_priority_fee_per_gas(keyword()) :: {:ok, binary()} | error()

@callback eth_get_logs(map(), keyword()) :: {:ok, [binary()] | [map()]} | error()
end
34 changes: 34 additions & 0 deletions lib/ethers/rpc_client/ethereumex_http_client.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
defmodule Ethers.RpcClient.EthereumexHttpClient do
@behaviour Ethers.RpcClient.Adapter

@exclude_delegation [:eth_get_logs]

for {func, arity} <- Ethers.RpcClient.Adapter.behaviour_info(:callbacks),
func not in @exclude_delegation do
args = Macro.generate_arguments(arity, __MODULE__)
@impl true
def unquote(func)(unquote_splicing(args)) do
apply(Ethereumex.HttpClient, unquote(func), [unquote_splicing(args)])
end
end

@impl true
def eth_get_logs(params, opts) do
params
|> replace_key(:from_block, :fromBlock)
|> replace_key(:to_block, :toBlock)
|> Ethereumex.HttpClient.eth_get_logs(opts)
end

defp replace_key(map, old_key, new_key) do
case Map.fetch(map, old_key) do
{:ok, value} ->
map
|> Map.put(new_key, value)
|> Map.delete(old_key)

:error ->
map
end
end
end
Loading