lib/w3ws/message.ex

defmodule W3WS.Message do
  @moduledoc """
  W3WS Messages for ethereum jsonrpc

  See [Ethereum JSON-RPC docs](https://ethereum.org/en/developers/docs/apis/json-rpc/)
  for more information.

  This is not meant to be an exhaustive list of all possible messages. Users are
  encouraged to use their own messages when necessary.
  """

  @jsonrpc "2.0"

  @doc """
  Create an `eth_blockNumber` request
  """
  def eth_block_number(opts \\ []) do
    %{
      jsonrpc: @jsonrpc,
      id: id(opts),
      method: :eth_blockNumber
    }
  end

  @doc """
  Create an `eth_getLogs` request.

  Takes a `params` argument which will be used as the `params` field in the message.
  """
  def eth_get_logs(opts \\ []) do
    filter =
      %{
        address: Keyword.get(opts, :address),
        topics: Keyword.get(opts, :topics),
        fromBlock: Keyword.get(opts, :from_block),
        toBlock: Keyword.get(opts, :to_block),
        blockHash: Keyword.get(opts, :block_hash)
      }
      |> strip_nil_values()

    # TODO: should we not include id in event if it wasn't
    # explicitly specified?
    %{
      jsonrpc: @jsonrpc,
      id: id(opts),
      method: :eth_getLogs,
      params: [filter]
    }
  end

  @doc """
  Create an `eth_subscribe` request

  Takes a `params` argument which will be used as the `params` field in the message.
  """
  def eth_subscribe(params, opts \\ []) do
    %{
      jsonrpc: @jsonrpc,
      id: id(opts),
      method: :eth_subscribe,
      params: params
    }
  end

  @doc """
  Create an `eth_subscribe` request for "logs".

  This is a convenience function and not part of the JSON-RPC spec.
  """
  def eth_subscribe_logs(topics, opts \\ []) do
    args =
      [topics: topics, address: Keyword.get(opts, :address)]
      |> strip_nil_values()

    eth_subscribe([:logs, args], opts)
  end

  @doc """
  Create an `eth_unsubscribe` request.

  Takes a `subscription` argument which will be used as the subscription 
  identifier in the request.
  """
  def eth_unsubscribe(subscription, opts \\ []) do
    %{
      jsonrpc: @jsonrpc,
      id: id(opts),
      method: :eth_unsubscribe,
      params: [subscription]
    }
  end

  @doc """
  Encode the message as json
  """
  def encode!(message), do: Jason.encode!(message)

  @doc """
  Dencode the message from json
  """
  def decode!(message), do: Jason.decode!(message)

  defp id(opts), do: Keyword.get(opts, :id, 1)

  @doc """
  Set the message id
  """
  def set_id(message, id), do: Map.put(message, :id, id)

  defp strip_nil_values(map) do
    map
    |> Enum.reject(fn {_k, v} -> is_nil(v) end)
    |> Enum.into(%{})
  end
end