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@ethereum@address.erl
Raw

src/gleeth@ethereum@address.erl

-module(gleeth@ethereum@address).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/gleeth/ethereum/address.gleam").
-export([to_lowercase/1, checksum/1, is_valid_checksum/1]).
-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(
" EIP-55 checksummed Ethereum addresses.\n"
"\n"
" Ethereum addresses are 20-byte hex strings. EIP-55 defines a mixed-case\n"
" encoding that serves as a checksum: each hex character is uppercased if\n"
" the corresponding nibble of the keccak256 hash of the lowercase address\n"
" is >= 8.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" address.checksum(\"0xd8da6bf26964af9d7eed9e03e53415d37aa96045\")\n"
" // -> Ok(\"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045\")\n"
" ```\n"
).
-file("src/gleeth/ethereum/address.gleam", 55).
?DOC(" Convert a potentially checksummed address to lowercase with 0x prefix.\n").
-spec to_lowercase(binary()) -> {ok, binary()} | {error, binary()}.
to_lowercase(Address) ->
Cleaned = case gleam_stdlib:string_starts_with(Address, <<"0x"/utf8>>) of
true ->
gleam@string:drop_start(Address, 2);
false ->
Address
end,
case string:length(Cleaned) of
40 ->
{ok, <<"0x"/utf8, (string:lowercase(Cleaned))/binary>>};
_ ->
{error, <<"Address must be 40 hex characters"/utf8>>}
end.
-file("src/gleeth/ethereum/address.gleam", 85).
-spec is_letter(binary()) -> boolean().
is_letter(Char) ->
case Char of
<<"a"/utf8>> ->
true;
<<"b"/utf8>> ->
true;
<<"c"/utf8>> ->
true;
<<"d"/utf8>> ->
true;
<<"e"/utf8>> ->
true;
<<"f"/utf8>> ->
true;
_ ->
false
end.
-file("src/gleeth/ethereum/address.gleam", 92).
-spec nibble_value(binary()) -> integer().
nibble_value(Char) ->
case Char of
<<"0"/utf8>> ->
0;
<<"1"/utf8>> ->
1;
<<"2"/utf8>> ->
2;
<<"3"/utf8>> ->
3;
<<"4"/utf8>> ->
4;
<<"5"/utf8>> ->
5;
<<"6"/utf8>> ->
6;
<<"7"/utf8>> ->
7;
<<"8"/utf8>> ->
8;
<<"9"/utf8>> ->
9;
<<"a"/utf8>> ->
10;
<<"b"/utf8>> ->
11;
<<"c"/utf8>> ->
12;
<<"d"/utf8>> ->
13;
<<"e"/utf8>> ->
14;
<<"f"/utf8>> ->
15;
_ ->
0
end.
-file("src/gleeth/ethereum/address.gleam", 66).
-spec apply_checksum(binary()) -> binary().
apply_checksum(Lower_hex) ->
Hash = gleeth@crypto@keccak:keccak256_binary(
gleam_stdlib:identity(Lower_hex)
),
Hash_hex = string:lowercase(gleam_stdlib:base16_encode(Hash)),
Addr_chars = gleam@string:to_graphemes(Lower_hex),
Hash_chars = gleam@string:to_graphemes(Hash_hex),
_pipe = gleam@list:map2(
Addr_chars,
Hash_chars,
fun(Addr_char, Hash_char) -> case is_letter(Addr_char) of
false ->
Addr_char;
true ->
case nibble_value(Hash_char) >= 8 of
true ->
string:uppercase(Addr_char);
false ->
Addr_char
end
end end
),
erlang:list_to_binary(_pipe).
-file("src/gleeth/ethereum/address.gleam", 21).
?DOC(" Produce the EIP-55 checksummed form of an Ethereum address.\n").
-spec checksum(binary()) -> {ok, binary()} | {error, binary()}.
checksum(Address) ->
Cleaned = case gleam_stdlib:string_starts_with(Address, <<"0x"/utf8>>) of
true ->
gleam@string:drop_start(Address, 2);
false ->
Address
end,
case string:length(Cleaned) of
40 ->
{ok,
<<"0x"/utf8,
(apply_checksum(string:lowercase(Cleaned)))/binary>>};
_ ->
{error, <<"Address must be 40 hex characters"/utf8>>}
end.
-file("src/gleeth/ethereum/address.gleam", 35).
?DOC(
" Check whether a mixed-case address has a valid EIP-55 checksum.\n"
" All-lowercase and all-uppercase addresses are considered valid\n"
" (no checksum applied).\n"
).
-spec is_valid_checksum(binary()) -> boolean().
is_valid_checksum(Address) ->
Cleaned = case gleam_stdlib:string_starts_with(Address, <<"0x"/utf8>>) of
true ->
gleam@string:drop_start(Address, 2);
false ->
Address
end,
case string:length(Cleaned) of
40 ->
Lower = string:lowercase(Cleaned),
Upper = string:uppercase(Cleaned),
case (Cleaned =:= Lower) orelse (Cleaned =:= Upper) of
true ->
true;
false ->
apply_checksum(Lower) =:= Cleaned
end;
_ ->
false
end.