lib/abi/function.ex

defmodule Ethex.Abi.Function do
  @moduledoc """
  0xf167FcA5b9FeDf4E8baCAf8547225af93832ed6F

  https://matic-mumbai.chainstacklabs.com

  Ethex.call "https://matic-mumbai.chainstacklabs.com",
    "erc20", "0xf167FcA5b9FeDf4E8baCAf8547225af93832ed6F", "symbol"

  Ethex.call "https://matic-mumbai.chainstacklabs.com",
    "erc20", "0xf167FcA5b9FeDf4E8baCAf8547225af93832ed6F", "balanceOf",
    ["8CcF629e123D83112423c283998443829A291334" |> Base.decode16!(case: :mixed)]
  """
  alias Ethex.Abi.Abi
  alias Ethex.Blockchain.StateMethod

  @doc """
  call contract function

  NOTE: address SHOULD BE binary
  """
  @spec call(String.t(), String.t(), String.t(), String.t(), list()) ::
          {:ok, list()} | {:error, any()}
  def call(rpc, abi_name, address, fun_name, args \\ []) do
    with {:ok, selectors} <- Abi.get_selectors_by_name(abi_name),
         fs <- find_selector(fun_name, selectors),
         {:ok, "0x" <> returns} <-
           StateMethod.eth_call(rpc, %{
             to: address,
             data: "0x#{Base.encode16(ABI.encode(fs, args, :input), case: :lower)}"
           }) do
      {:ok, ABI.decode(fs, Base.decode16!(returns, case: :lower), :output)}
    else
      {:error, :not_found} -> {:error, :abi_not_register}
      nil -> {:error, :function_selector_not_found}
      error -> error
    end
  end

  defp find_selector(fun_name, selectors) do
    Enum.find(selectors, fn s -> s.type == :function and s.function == fun_name end)
  end
end