-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 = <>, 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 => <>, 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 ).