Packages
signet
1.0.0-charlie7
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/sleuth.ex
defmodule Signet.Sleuth do
@moduledoc ~S"""
Sleuth allows you to run a contract call as a single
`eth_call` call.
Note: Signet.Contract.Sleuth generated from `mix signet.gen --prefix signet/contract ./priv/Sleuth.json`
"""
use Signet.Hex
@sleuth_address ~h[0xc6a613fdac3465d250df7ff3cc21bec86eb8a372]
def query(bytecode, query, selector, opts \\ []),
do: query_internal(bytecode, query, selector, false, opts)
def query_annotated(bytecode, query, selector, opts \\ []),
do: query_internal(bytecode, query, selector, true, opts)
def query_by(mod, fun) when is_atom(mod) and is_atom(fun), do: query_by(mod, fun, [])
def query_by(mod, opts) when is_atom(mod) and is_list(opts), do: query_by(mod, :query, opts)
def query_by(mod), do: query_by(mod, :query, [])
def query_by(mod, fun, opts) when is_atom(mod) and is_atom(fun) and is_list(opts) do
bytecode = try_apply(mod, :bytecode, [])
query_val = try_apply(mod, String.to_atom("encode_" <> to_string(fun)), [])
selector = try_apply(mod, String.to_atom(to_string(fun) <> "_selector"), [])
query_internal(bytecode, query_val, selector, false, opts)
end
defp query_internal(bytecode, query, selector, annotated, opts)
when is_binary(bytecode) and is_list(opts) do
{sleuth_address, rpc_opts} = Keyword.pop(opts, :sleuth_address, @sleuth_address)
with {:ok, query_res_bytes} <-
Signet.Contract.Sleuth.call_query(sleuth_address, bytecode, query, rpc_opts),
{:ok, query_res} <- try_decode_bytes(query_res_bytes),
{:ok, res} <- try_decode(query_res, selector) do
{:ok, unwrap_outer_tuple(encode_map(res, selector.returns, annotated))}
end
end
defp try_decode_bytes(bytes) do
try do
[decoded] = ABI.decode("(bytes)", bytes)
{:ok, decoded}
rescue
e ->
{:error, "error decoding bytes: #{inspect(e)}"}
end
end
defp try_decode(query_res, selector) do
try do
{:ok, ABI.decode(%ABI.FunctionSelector{types: selector.returns}, query_res)}
rescue
e ->
{:error, "error decoding: #{inspect(e)}"}
end
end
defp encode_map(res, types, annotated) do
Enum.map(Enum.zip(res, Enum.with_index(types)), fn {res, {type, i}} ->
encode_item(res, type, i, annotated)
end)
|> Enum.into(%{})
end
defp encode_item(res, type, i, annotated) do
var_name =
if is_nil(type.name) or type.name == "" do
"var#{i}"
else
type.name
end
res_enc = encode_value(res, type.type, annotated)
{var_name, res_enc}
end
defp encode_value(res, type, annotated) do
encode_array = fn sub_type ->
Enum.map(res, fn r -> encode_value(r, sub_type, annotated) end)
end
case type do
{:tuple, sub_types} ->
encode_map(Tuple.to_list(res), sub_types, annotated)
{:array, sub_type} ->
encode_array.(sub_type)
{:array, sub_type, _} ->
encode_array.(sub_type)
_ ->
if annotated do
{type, res}
else
res
end
end
end
defp unwrap_outer_tuple(%{"var0" => x}), do: x
defp unwrap_outer_tuple(els), do: els
defp try_apply(mod, fun, args) do
try do
apply(mod, fun, args)
rescue
_ ->
raise "Sleuth module #{mod} does not define required \"#{fun}/#{Enum.count(args)}\" function"
end
end
end