Packages
signet
1.2.3
1.6.1
1.6.0
1.5.0
1.4.2
1.4.1
1.4.0
1.3.12
1.3.11
1.3.10
1.3.9
1.3.8
1.3.7
1.3.6
1.3.5
1.3.4
1.3.3
1.3.2
1.3.1
1.3.0
1.2.9
1.2.8
1.2.7
1.2.6
1.2.5
1.2.4
1.2.3
1.2.2
1.2.1
1.2.0
1.1.2
1.1.1
1.1.0
1.0.0-echo8
1.0.0-echo7
1.0.0-echo6
1.0.0-echo5
1.0.0-echo4
1.0.0-echo3
1.0.0-echo2
1.0.0-echo1
1.0.0-delta8
1.0.0-delta7
1.0.0-delta6
1.0.0-delta5
1.0.0-delta4
1.0.0-delta3
1.0.0-delta2
1.0.0-delta1
1.0.0-charlie9
1.0.0-charlie8
1.0.0-charlie7
1.0.0-charlie6
1.0.0-charlie5
1.0.0-charlie4
1.0.0-charlie3
1.0.0-charlie2
1.0.0-charlie1
1.0.0-beta9
1.0.0-beta8
1.0.0-beta7
1.0.0-beta6
1.0.0-beta5
1.0.0-beta4
1.0.0-beta3
1.0.0-beta2
1.0.0-beta1
1.0.0-alpha9
1.0.0-alpha8
1.0.0-alpha7
1.0.0-alpha6
1.0.0-alpha5
1.0.0-alpha4
1.0.0-alpha3
1.0.0-alpha2
1.0.0-alpha10
1.0.0-alpha1
0.2.0-alpha6
0.2.0-alpha5
0.2.0-alpha4
0.2.0-alpha1
0.1.10
0.1.9
0.1.8
0.1.7
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
0.1.0-rc7
0.1.0-rc6
0.1.0-rc5
0.1.0-rc4
0.1.0-rc3
0.1.0-rc2
0.1.0-rc1
0.1.0-rc0
Lightweight Ethereum and Solana RPC client for Elixir
Current section
Files
Jump to
Current section
Files
lib/signet/open_chain.ex
defmodule Signet.OpenChain do
@moduledoc ~S"""
API Client for [OpenChain.xyz](https://openchain.xyz] API.
"""
use Signet.Hex
defmodule Signatures do
defstruct [:events, :functions]
@type t :: %__MODULE__{
events: [{binary(), String.t()}],
functions: [{binary(), String.t()}]
}
@doc ~S"""
Deserializes an open chain signature.
## Examples
iex> %{
...> "event" => %{
...> "0x08c379a0" => []
...> },
...> "function" => %{
...> "0x08c379a0" => [
...> %{
...> "name" => "Error(string)",
...> "filtered" => false
...> }
...> ]
...> }
...> }
...> |> Signet.OpenChain.Signatures.deserialize()
%Signet.OpenChain.Signatures{
events: [],
functions: [
{<<8, 195, 121, 160>>, "Error(string)"}
]
}
"""
def deserialize(%{"event" => event_list, "function" => function_list}) do
%__MODULE__{
events: decode_entries(event_list),
functions: decode_entries(function_list)
}
end
defp decode_entries(entries) when is_map(entries) do
entries
|> Enum.reject(fn {_k, v} -> is_nil(v) end)
|> Enum.map(fn
{k, vs} ->
vs
|> Enum.filter(fn v -> not v["filtered"] end)
|> Enum.map(fn v -> {from_hex!(k), v["name"]} end)
end)
|> List.flatten()
end
end
defmodule API do
def http_client(), do: Application.get_env(:signet, :open_chain_client, HTTPoison)
@base_url Application.compile_env(:signet, :open_chain_base_url, "https://api.openchain.xyz")
@spec get(String.t(), Keyword.t()) :: {:ok, term()} | {:error, String.t()}
def get(url, opts) do
headers = Keyword.get(opts, :headers, [])
timeout = Keyword.get(opts, :timeout, 30_000)
case http_client().get(url, headers, recv_timeout: timeout) do
{:ok, %HTTPoison.Response{status_code: code, body: resp_body}} when code in 200..299 ->
case Jason.decode(resp_body) do
{:ok, resp} ->
case resp do
%{"ok" => true, "result" => result} ->
{:ok, result}
%{"ok" => false, "error" => error} ->
{:error, error}
end
{:error, json_error} ->
{:error, Jason.DecodeError.message(json_error)}
end
{:error, %HTTPoison.Error{reason: reason}} ->
{:error, "error: #{inspect(reason)}"}
end
end
@doc ~S"""
Runs a lookup query from OpenChain, returning matching signtures.
## Examples
iex> Signet.OpenChain.API.lookup([<<8, 195, 121, 160>>], [])
{:ok,
%Signet.OpenChain.Signatures{
events: [],
functions: [{<<8, 195, 121, 160>>, "Error(string)"}]
}
}
"""
@spec lookup([binary()], [binary()], Keyword.t()) ::
{:ok, Signatures.t()} | {:error, String.t()}
def lookup(event_signatures, function_signatures, opts \\ []) do
{filter, opts} = Keyword.pop(opts, :filter, true)
events =
event_signatures
|> Enum.map(&Signet.Hex.to_hex/1)
|> Enum.join(",")
functions =
function_signatures
|> Enum.map(&Signet.Hex.to_hex/1)
|> Enum.join(",")
with {:ok, resp} <-
get(
"#{@base_url}/signature-database/v1/lookup?#{URI.encode_query(event: events, function: functions, filter: filter)}",
opts
) do
{:ok, Signatures.deserialize(resp)}
end
end
end
@doc ~S"""
Tries to lookup given signature of given type.
## Examples
iex> Signet.OpenChain.lookup(<<8, 195, 121, 160>>, :function)
{:ok, "Error(string)"}
"""
def lookup(signature, type, opts \\ []) do
{raise_on_multiple, opts} = Keyword.pop(opts, :raise_on_multiple, false)
found_signatures_result =
case type do
:function ->
with {:ok, signatures} <- API.lookup([], [signature], opts) do
{:ok, signatures.functions}
end
:event ->
with {:ok, signatures} <- API.lookup([signature], [], opts) do
{:ok, signatures.events}
end
end
with {:ok, found_signatures} <- found_signatures_result do
case Enum.count(found_signatures) do
0 ->
{:error, "Signature not found"}
x when x == 1 or not raise_on_multiple ->
{^signature, abi} = List.first(found_signatures)
{:ok, abi}
_ ->
{:error, "Multiple matching signatures: #{Enum.join(found_signatures, ",")}"}
end
end
end
@doc ~S"""
Looks up and tries to decode a given error message from its ABI-encoded form.
## Examples
iex> Signet.OpenChain.lookup_error(~h[0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001b43616c6c6572206e6f74206174746573746572206d616e616765720000000000])
{:ok, ["Caller not attester manager"]}
"""
def lookup_error(_, opts \\ [])
def lookup_error(<<signature::binary-size(4), data::binary>>, opts) do
with {:ok, signature} <- lookup(signature, :function, opts),
function_selector <- ABI.FunctionSelector.decode(signature),
result <- ABI.decode(function_selector, data) do
{:ok, result}
end
end
def lookup_error(_, _opts), do: {:error, "Error must include 4-byte signature"}
@doc """
Looks up an error and decodes its values, returning both.
## Examples
iex> Signet.OpenChain.lookup_error_and_values(~h[0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001b43616c6c6572206e6f74206174746573746572206d616e616765720000000000])
{:ok, "Error(string)", ["Caller not attester manager"]}
"""
def lookup_error_and_values(_, opts \\ [])
def lookup_error_and_values(<<signature::binary-size(4), data::binary>>, opts) do
with {:ok, signature} <- lookup(signature, :function, opts),
function_selector <- ABI.FunctionSelector.decode(signature),
result <- ABI.decode(function_selector, data) do
{:ok, signature, result}
end
end
def lookup_error_and_values(_, _opts), do: {:error, "Error must include 4-byte signature"}
end