Packages

Ethereum blockchain client for Gleam

Current section

Files

Jump to
glethers src glethers@hash@typed_data.erl
Raw

src/glethers@hash@typed_data.erl

-module(glethers@hash@typed_data).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([encode_data/1, hash_struct/1, hash_domain/1, hash_message/3, encode_type/1]).
-export_type([typed_data_domain/0, complex_field/0, encode_field/0]).
-type typed_data_domain() :: {typed_data_domain,
gleam@option:option(binary()),
gleam@option:option(binary()),
gleam@option:option(glethers@primitives@integer:uint256()),
gleam@option:option(glethers@address:address()),
gleam@option:option(glethers@primitives@bytes:bytes32())}.
-type complex_field() :: {primitive, glethers@primitives:primitive()} |
{struct, {binary(), list({binary(), complex_field()})}}.
-type encode_field() :: name | version | chain_id | verifying_contract | salt.
-spec do_domain_to_typed_data(typed_data_domain(), encode_field()) -> list({binary(),
complex_field()}).
do_domain_to_typed_data(Domain, Field) ->
case Field of
name ->
case erlang:element(2, Domain) of
{some, Name} ->
[{<<"name"/utf8>>,
{primitive, glethers@primitives:from_string(Name)}} |
do_domain_to_typed_data(Domain, version)];
none ->
do_domain_to_typed_data(Domain, version)
end;
version ->
case erlang:element(3, Domain) of
{some, Version} ->
[{<<"version"/utf8>>,
{primitive,
glethers@primitives:from_string(Version)}} |
do_domain_to_typed_data(Domain, chain_id)];
none ->
do_domain_to_typed_data(Domain, chain_id)
end;
chain_id ->
case erlang:element(4, Domain) of
{some, Chain_id} ->
[{<<"chainId"/utf8>>,
{primitive,
glethers@primitives:from_uint256(Chain_id)}} |
do_domain_to_typed_data(Domain, verifying_contract)];
none ->
do_domain_to_typed_data(Domain, verifying_contract)
end;
verifying_contract ->
case erlang:element(5, Domain) of
{some, Verifying_contract} ->
[{<<"verifyingContract"/utf8>>,
{primitive,
glethers@primitives:from_address(
Verifying_contract
)}} |
do_domain_to_typed_data(Domain, salt)];
none ->
do_domain_to_typed_data(Domain, salt)
end;
salt ->
case erlang:element(6, Domain) of
{some, Salt} ->
[{<<"salt"/utf8>>,
{primitive, glethers@primitives:from_bytes32(Salt)}}];
none ->
[]
end
end.
-spec domain_to_typed_data(typed_data_domain()) -> {binary(),
list({binary(), complex_field()})}.
domain_to_typed_data(Domain) ->
Typed_values = begin
_pipe = Domain,
do_domain_to_typed_data(_pipe, name)
end,
{<<"EIP712Domain"/utf8>>, Typed_values}.
-spec encode_data({binary(), list({binary(), complex_field()})}) -> bitstring().
encode_data(Values) ->
do_encode_data(erlang:element(2, Values)).
-spec do_encode_data(list({binary(), complex_field()})) -> bitstring().
do_encode_data(Values) ->
case Values of
[] ->
<<>>;
[{_, Value} | Rest] ->
Encoded_value = case Value of
{primitive, Prim} ->
_pipe = Prim,
glethers@primitives:eip712_encode(_pipe);
{struct, Struct} ->
_pipe@1 = Struct,
hash_struct(_pipe@1)
end,
<<Encoded_value/bitstring, (do_encode_data(Rest))/bitstring>>
end.
-spec hash_struct({binary(), list({binary(), complex_field()})}) -> bitstring().
hash_struct(Data) ->
Encoded_type = begin
_pipe = encode_type(Data),
gleam_stdlib:identity(_pipe)
end,
Encoded_type_hash = 'Elixir.ExKeccak':hash_256(Encoded_type),
Encoded_data = encode_data(Data),
'Elixir.ExKeccak':hash_256(
gleam_stdlib:bit_array_concat([Encoded_type_hash, Encoded_data])
).
-spec hash_domain(typed_data_domain()) -> bitstring().
hash_domain(Domain) ->
_pipe = Domain,
_pipe@1 = domain_to_typed_data(_pipe),
hash_struct(_pipe@1).
-spec encode(typed_data_domain(), {binary(), list({binary(), complex_field()})}) -> bitstring().
encode(Domain, Values) ->
gleam_stdlib:bit_array_concat(
[<<16#19, 16#01>>, hash_domain(Domain), hash_struct(Values)]
).
-spec hash_message(
typed_data_domain(),
HPT,
fun((HPT) -> {binary(), list({binary(), complex_field()})})
) -> glethers@hash@message:hash().
hash_message(Domain, Struct, Encoder) ->
Typed_data = Encoder(Struct),
_assert_subject = glethers@hash@message:from_bit_array(
'Elixir.ExKeccak':hash_256(encode(Domain, Typed_data))
),
{ok, Hash} = case _assert_subject of
{ok, _} -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"glethers/hash/typed_data"/utf8>>,
function => <<"hash_message"/utf8>>,
line => 186})
end,
Hash.
-spec encode_type({binary(), list({binary(), complex_field()})}) -> binary().
encode_type(Typed_data) ->
{Types, Result_dict} = do_encode_type(erlang:element(2, Typed_data)),
Struct_types = begin
_pipe = Result_dict,
_pipe@1 = maps:to_list(_pipe),
_pipe@2 = gleam@list:map(_pipe@1, fun(X) -> erlang:element(2, X) end),
_pipe@3 = gleam@list:sort(_pipe@2, fun gleam@string:compare/2),
gleam@list:map(_pipe@3, fun gleam@string_builder:from_string/1)
end,
Builder = begin
_pipe@4 = gleam@string_builder:from_string(
<<(erlang:element(1, Typed_data))/binary, "("/utf8>>
),
_pipe@5 = gleam@string_builder:append_builder(
_pipe@4,
gleam@string_builder:join(Types, <<","/utf8>>)
),
gleam@string_builder:append(_pipe@5, <<")"/utf8>>)
end,
_pipe@6 = gleam@string_builder:concat([Builder | Struct_types]),
gleam@string_builder:to_string(_pipe@6).
-spec do_encode_type(list({binary(), complex_field()})) -> {list(gleam@string_builder:string_builder()),
gleam@dict:dict(binary(), binary())}.
do_encode_type(Values) ->
case Values of
[] ->
{[], gleam@dict:new()};
[{Key, Value} | Rest] ->
{Type_, New_types} = case Value of
{primitive, {string, _}} ->
{<<"string"/utf8>>, none};
{primitive, {address, _}} ->
{<<"address"/utf8>>, none};
{primitive, {uint256, _}} ->
{<<"uint256"/utf8>>, none};
{primitive, {bytes32, _}} ->
{<<"bytes32"/utf8>>, none};
{struct, Struct} ->
Struct_name = encode_type(Struct),
{Name, _} = Struct,
{Name, {some, Struct_name}}
end,
{Rest@1, Names} = do_encode_type(Rest),
Result_dict = case New_types of
{some, Name@1} ->
_pipe = Names,
gleam@dict:insert(_pipe, Type_, Name@1);
none ->
Names
end,
{[gleam@string_builder:from_string(
<<<<Type_/binary, " "/utf8>>/binary, Key/binary>>
) |
Rest@1],
Result_dict}
end.