Packages

Ethereum library for Gleam - JSON-RPC client, transaction signing, ABI encoding, and wallet management on the BEAM

Current section

Files

Jump to
gleeth src gleeth@ens.erl
Raw

src/gleeth@ens.erl

-module(gleeth@ens).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/gleeth/ens.gleam").
-export([namehash/1, resolve/2]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" ENS (Ethereum Name Service) resolution.\n"
"\n"
" Resolves human-readable `.eth` names to Ethereum addresses by querying\n"
" the ENS registry and resolver contracts on-chain.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let assert Ok(address) = ens.resolve(provider, \"vitalik.eth\")\n"
" // address = \"0xd8da6bf26964af9d7eed9e03e53415d37aa96045\"\n"
" ```\n"
).
-file("src/gleeth/ens.gleam", 86).
?DOC(
" Compute the ENS namehash for a domain name.\n"
"\n"
" The namehash algorithm recursively hashes labels:\n"
" `namehash(\"\") = 0x00...00`\n"
" `namehash(\"eth\") = keccak256(namehash(\"\") + keccak256(\"eth\"))`\n"
" `namehash(\"vitalik.eth\") = keccak256(namehash(\"eth\") + keccak256(\"vitalik\"))`\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let hash = ens.namehash(\"vitalik.eth\")\n"
" ```\n"
).
-spec namehash(binary()) -> bitstring().
namehash(Name) ->
case Name of
<<""/utf8>> ->
<<0:256>>;
_ ->
Labels = gleam@string:split(Name, <<"."/utf8>>),
Reversed = lists:reverse(Labels),
gleam@list:fold(
Reversed,
<<0:256>>,
fun(Node, Label) ->
Label_hash = gleeth@crypto@keccak:keccak256_binary(
gleam_stdlib:identity(Label)
),
gleeth@crypto@keccak:keccak256_binary(
gleam_stdlib:bit_array_concat([Node, Label_hash])
)
end
)
end.
-file("src/gleeth/ens.gleam", 105).
?DOC(" Call a function that takes a bytes32 arg and returns an address.\n").
-spec call_address(gleeth@provider:provider(), binary(), binary(), binary()) -> {ok,
binary()} |
{error, gleeth@rpc@types:gleeth_error()}.
call_address(Provider, Contract, Selector, Arg_hex) ->
Arg_clean = gleeth@utils@hex:strip_prefix(Arg_hex),
Padded = gleam@string:pad_start(Arg_clean, 64, <<"0"/utf8>>),
Calldata = <<Selector/binary, Padded/binary>>,
gleam@result:'try'(
gleeth@rpc@methods:call_contract(Provider, Contract, Calldata),
fun(Result_hex) -> case gleeth@utils@hex:decode(Result_hex) of
{ok, Bytes} ->
Size = erlang:byte_size(Bytes),
case Size >= 32 of
true ->
Addr_bytes@1 = case gleam_stdlib:bit_array_slice(
Bytes,
12,
20
) of
{ok, Addr_bytes} -> Addr_bytes;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"gleeth/ens"/utf8>>,
function => <<"call_address"/utf8>>,
line => 128,
value => _assert_fail,
start => 3716,
'end' => 3774,
pattern_start => 3727,
pattern_end => 3741})
end,
Addr = <<"0x"/utf8,
(string:lowercase(
gleam_stdlib:base16_encode(Addr_bytes@1)
))/binary>>,
{ok, Addr};
false ->
{ok, <<""/utf8>>}
end;
{error, _} ->
{ok, <<""/utf8>>}
end end
).
-file("src/gleeth/ens.gleam", 37).
?DOC(
" Resolve an ENS name to an Ethereum address.\n"
"\n"
" Queries the ENS registry for the resolver, then queries the resolver\n"
" for the address. Returns an error if the name has no resolver or no\n"
" address set.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let assert Ok(addr) = ens.resolve(provider, \"vitalik.eth\")\n"
" ```\n"
).
-spec resolve(gleeth@provider:provider(), binary()) -> {ok, binary()} |
{error, gleeth@rpc@types:gleeth_error()}.
resolve(Provider, Name) ->
Node = namehash(Name),
Node_hex = gleeth@utils@hex:encode(Node),
gleam@result:'try'(
call_address(
Provider,
<<"0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e"/utf8>>,
<<"0x0178b8bf"/utf8>>,
Node_hex
),
fun(Resolver_address) -> case Resolver_address of
<<""/utf8>> ->
{error,
{parse_error,
<<"No resolver found for: "/utf8, Name/binary>>}};
<<"0x0000000000000000000000000000000000000000"/utf8>> ->
{error,
{parse_error,
<<"No resolver found for: "/utf8, Name/binary>>}};
Resolver ->
gleam@result:'try'(
call_address(
Provider,
Resolver,
<<"0x3b3b57de"/utf8>>,
Node_hex
),
fun(Address) -> case Address of
<<""/utf8>> ->
{error,
{parse_error,
<<"No address set for: "/utf8,
Name/binary>>}};
<<"0x0000000000000000000000000000000000000000"/utf8>> ->
{error,
{parse_error,
<<"No address set for: "/utf8,
Name/binary>>}};
Addr ->
{ok, Addr}
end end
)
end end
).