mirror of https://github.com/poanetwork/ex_abi.git
Compare commits
2 Commits
c8056a8a1b
...
36d4e62694
Author | SHA1 | Date |
---|---|---|
Ayrat Badykov | 36d4e62694 | |
Alisina Bahadori | 586bada399 |
|
@ -1,6 +1,8 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## 0.6.2
|
## 0.6.4
|
||||||
|
* Implement Packed encoding (https://github.com/poanetwork/ex_abi/pull/154)
|
||||||
|
## 0.6.3
|
||||||
* Add return_names to the FunctionSelector struct (https://github.com/poanetwork/ex_abi/pull/151)
|
* Add return_names to the FunctionSelector struct (https://github.com/poanetwork/ex_abi/pull/151)
|
||||||
## 0.6.2
|
## 0.6.2
|
||||||
* Update ex_keccak to 0.7.3 (https://github.com/poanetwork/ex_abi/pull/146)
|
* Update ex_keccak to 0.7.3 (https://github.com/poanetwork/ex_abi/pull/146)
|
||||||
|
|
|
@ -10,7 +10,7 @@ by adding `ex_abi` to your list of dependencies in `mix.exs`:
|
||||||
```elixir
|
```elixir
|
||||||
def deps do
|
def deps do
|
||||||
[
|
[
|
||||||
{:ex_abi, "~> 0.6.3"}
|
{:ex_abi, "~> 0.6.4"}
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
28
lib/abi.ex
28
lib/abi.ex
|
@ -58,7 +58,33 @@ defmodule ABI do
|
||||||
end
|
end
|
||||||
|
|
||||||
def encode(%FunctionSelector{} = function_selector, data, data_type) do
|
def encode(%FunctionSelector{} = function_selector, data, data_type) do
|
||||||
TypeEncoder.encode(data, function_selector, data_type)
|
TypeEncoder.encode(data, function_selector, data_type, :standard)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Encodes the given data into the given types in packed encoding mode.
|
||||||
|
|
||||||
|
Note that packed encoding mode is ambiguous and cannot be decoded (there are no decode_packed functions).
|
||||||
|
Also, tuples (structs) and nester arrays are not supported.
|
||||||
|
|
||||||
|
More info https://docs.soliditylang.org/en/latest/abi-spec.html#non-standard-packed-mode
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> ABI.encode_packed([{:uint, 16}], [0x12])
|
||||||
|
...> |> Base.encode16(case: :lower)
|
||||||
|
"0012"
|
||||||
|
|
||||||
|
iex> ABI.encode_packed([:string, {:uint, 16}], ["Elixir ABI", 0x12])
|
||||||
|
...> |> Base.encode16(case: :lower)
|
||||||
|
"456c69786972204142490012"
|
||||||
|
|
||||||
|
iex> ABI.encode_packed([{:int, 16}, {:bytes, 1}, {:uint, 16}, :string], [-1, <<0x42>>, 0x03, "Hello, world!"])
|
||||||
|
...> |> Base.encode16(case: :lower)
|
||||||
|
"ffff42000348656c6c6f2c20776f726c6421"
|
||||||
|
"""
|
||||||
|
def encode_packed(types, data) when is_list(types) do
|
||||||
|
TypeEncoder.encode(data, types, :input, :packed)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
|
|
|
@ -9,33 +9,39 @@ defmodule ABI.TypeEncoder do
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Encodes the given data based on the function selector.
|
Encodes the given data based on the function selector.
|
||||||
|
|
||||||
|
## Parameters
|
||||||
|
- data: The data to encode
|
||||||
|
- selector_or_types: Either a FunctionSelector struct or a list of types to encode the data with
|
||||||
|
- data_type: Determines which types to use from a FunctionSelector struct. Can be `:input` or `:output`.
|
||||||
|
- mode: Encoding mode. Can be `:standard` or `:packed`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def encode(data, selector_or_types, data_type \\ :input)
|
def encode(data, selector_or_types, data_type \\ :input, mode \\ :standard)
|
||||||
|
|
||||||
def encode(data, %FunctionSelector{function: nil, types: types}, :input) do
|
def encode(data, %FunctionSelector{function: nil, types: types}, :input, mode) do
|
||||||
do_encode(data, types)
|
do_encode(data, types, mode)
|
||||||
end
|
end
|
||||||
|
|
||||||
def encode(data, %FunctionSelector{types: types} = function_selector, :input) do
|
def encode(data, %FunctionSelector{types: types} = function_selector, :input, mode) do
|
||||||
encode_method_id(function_selector) <> do_encode(data, types)
|
encode_method_id(function_selector) <> do_encode(data, types, mode)
|
||||||
end
|
end
|
||||||
|
|
||||||
def encode(data, %FunctionSelector{returns: types}, :output) do
|
def encode(data, %FunctionSelector{returns: types}, :output, mode) do
|
||||||
do_encode(data, types)
|
do_encode(data, types, mode)
|
||||||
end
|
end
|
||||||
|
|
||||||
def encode(data, types, _) when is_list(types) do
|
def encode(data, types, _, mode) when is_list(types) do
|
||||||
do_encode(data, types)
|
do_encode(data, types, mode)
|
||||||
end
|
end
|
||||||
|
|
||||||
def encode_raw(data, types) when is_list(types) do
|
def encode_raw(data, types, mode) when is_list(types) do
|
||||||
do_encode(data, types)
|
do_encode(data, types, mode)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp do_encode(params, types, static_acc \\ [], dynamic_acc \\ [])
|
defp do_encode(params, types, static_acc \\ [], dynamic_acc \\ [], mode)
|
||||||
|
|
||||||
defp do_encode([], [], reversed_static_acc, reversed_dynamic_acc) do
|
defp do_encode([], [], reversed_static_acc, reversed_dynamic_acc, :standard) do
|
||||||
static_acc = Enum.reverse(reversed_static_acc)
|
static_acc = Enum.reverse(reversed_static_acc)
|
||||||
dynamic_acc = Enum.reverse(reversed_dynamic_acc)
|
dynamic_acc = Enum.reverse(reversed_dynamic_acc)
|
||||||
|
|
||||||
|
@ -61,104 +67,131 @@ defmodule ABI.TypeEncoder do
|
||||||
{complete_static_part, _} =
|
{complete_static_part, _} =
|
||||||
Enum.reduce(dynamic_indexes, {static_acc, static_part_size}, fn {index, byte_size},
|
Enum.reduce(dynamic_indexes, {static_acc, static_part_size}, fn {index, byte_size},
|
||||||
{acc, size_acc} ->
|
{acc, size_acc} ->
|
||||||
new_static_acc = List.replace_at(acc, index, encode_uint(size_acc, 256))
|
new_static_acc = List.replace_at(acc, index, encode_uint(size_acc, 256, :standard))
|
||||||
new_prefix_size = byte_size + size_acc
|
|
||||||
|
|
||||||
{new_static_acc, new_prefix_size}
|
{new_static_acc, byte_size + size_acc}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
Enum.join(complete_static_part ++ dynamic_acc)
|
Enum.join(complete_static_part ++ dynamic_acc)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp do_encode([], [], static_acc, dynamic_acc, :packed) do
|
||||||
|
{values_acc, []} =
|
||||||
|
Enum.reduce(static_acc, {[], dynamic_acc}, fn
|
||||||
|
{:dynamic, _}, {values_acc, [value | dynamic_acc]} ->
|
||||||
|
{[value | values_acc], dynamic_acc}
|
||||||
|
|
||||||
|
value, {values_acc, dynamic_acc} ->
|
||||||
|
{[value | values_acc], dynamic_acc}
|
||||||
|
end)
|
||||||
|
|
||||||
|
Enum.join(values_acc)
|
||||||
|
end
|
||||||
|
|
||||||
defp do_encode(
|
defp do_encode(
|
||||||
[current_parameter | remaining_parameters],
|
[current_parameter | remaining_parameters],
|
||||||
[current_type | remaining_types],
|
[current_type | remaining_types],
|
||||||
static_acc,
|
static_acc,
|
||||||
dynamic_acc
|
dynamic_acc,
|
||||||
|
mode
|
||||||
) do
|
) do
|
||||||
{new_static_acc, new_dynamic_acc} =
|
{new_static_acc, new_dynamic_acc} =
|
||||||
do_encode_type(current_type, current_parameter, static_acc, dynamic_acc)
|
do_encode_type(current_type, current_parameter, static_acc, dynamic_acc, mode)
|
||||||
|
|
||||||
do_encode(remaining_parameters, remaining_types, new_static_acc, new_dynamic_acc)
|
do_encode(remaining_parameters, remaining_types, new_static_acc, new_dynamic_acc, mode)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp do_encode_type(:bool, parameter, static_part, dynamic_part) do
|
defp do_encode_type(:bool, parameter, static_part, dynamic_part, mode) do
|
||||||
value =
|
value =
|
||||||
case parameter do
|
case parameter do
|
||||||
true -> encode_uint(1, 8)
|
true -> encode_uint(1, 8, mode)
|
||||||
false -> encode_uint(0, 8)
|
false -> encode_uint(0, 8, mode)
|
||||||
_ -> raise "Invalid data for bool: #{inspect(parameter)}"
|
_ -> raise "Invalid data for bool: #{inspect(parameter)}"
|
||||||
end
|
end
|
||||||
|
|
||||||
{[value | static_part], dynamic_part}
|
{[value | static_part], dynamic_part}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp do_encode_type({:uint, size}, parameter, static_part, dynamic_part) do
|
defp do_encode_type({:uint, size}, parameter, static_part, dynamic_part, mode) do
|
||||||
value = encode_uint(parameter, size)
|
value = encode_uint(parameter, size, mode)
|
||||||
|
|
||||||
{[value | static_part], dynamic_part}
|
{[value | static_part], dynamic_part}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp do_encode_type({:int, size}, parameter, static_part, dynamic_part) do
|
defp do_encode_type({:int, size}, parameter, static_part, dynamic_part, mode) do
|
||||||
value = encode_int(parameter, size)
|
value = encode_int(parameter, size, mode)
|
||||||
|
|
||||||
{[value | static_part], dynamic_part}
|
{[value | static_part], dynamic_part}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp do_encode_type(:string, parameter, static_part, dynamic_part) do
|
defp do_encode_type(:string, parameter, static_part, dynamic_part, mode) do
|
||||||
do_encode_type(:bytes, parameter, static_part, dynamic_part)
|
do_encode_type(:bytes, parameter, static_part, dynamic_part, mode)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp do_encode_type(:bytes, parameter, static_part, dynamic_part) do
|
defp do_encode_type(:bytes, parameter, static_part, dynamic_part, mode) do
|
||||||
binary_param = maybe_encode_unsigned(parameter)
|
binary_param = maybe_encode_unsigned(parameter)
|
||||||
value = encode_uint(byte_size(binary_param), 256) <> encode_bytes(binary_param)
|
|
||||||
|
value =
|
||||||
|
case mode do
|
||||||
|
:standard ->
|
||||||
|
encode_uint(byte_size(binary_param), 256, mode) <> encode_bytes(binary_param, mode)
|
||||||
|
|
||||||
|
:packed ->
|
||||||
|
encode_bytes(binary_param, mode)
|
||||||
|
end
|
||||||
|
|
||||||
dynamic_part_byte_size = byte_size(value)
|
dynamic_part_byte_size = byte_size(value)
|
||||||
|
|
||||||
{[{:dynamic, dynamic_part_byte_size} | static_part], [value | dynamic_part]}
|
{[{:dynamic, dynamic_part_byte_size} | static_part], [value | dynamic_part]}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp do_encode_type({:bytes, size}, parameter, static_part, dynamic_part)
|
defp do_encode_type({:bytes, size}, parameter, static_part, dynamic_part, mode)
|
||||||
when is_binary(parameter) and byte_size(parameter) <= size do
|
when is_binary(parameter) and byte_size(parameter) <= size do
|
||||||
value = encode_bytes(parameter)
|
value = encode_bytes(parameter, mode)
|
||||||
|
|
||||||
{[value | static_part], dynamic_part}
|
{[value | static_part], dynamic_part}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp do_encode_type({:bytes, size}, data, _, _) when is_binary(data) do
|
defp do_encode_type({:bytes, size}, data, _, _, _) when is_binary(data) do
|
||||||
raise "size mismatch for bytes#{size}: #{inspect(data)}"
|
raise "size mismatch for bytes#{size}: #{inspect(data)}"
|
||||||
end
|
end
|
||||||
|
|
||||||
defp do_encode_type({:bytes, size}, data, static_part, dynamic_part) when is_integer(data) do
|
defp do_encode_type({:bytes, size}, data, static_part, dynamic_part, mode)
|
||||||
|
when is_integer(data) do
|
||||||
binary_param = maybe_encode_unsigned(data)
|
binary_param = maybe_encode_unsigned(data)
|
||||||
|
|
||||||
do_encode_type({:bytes, size}, binary_param, static_part, dynamic_part)
|
do_encode_type({:bytes, size}, binary_param, static_part, dynamic_part, mode)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp do_encode_type({:bytes, size}, data, _, _) do
|
defp do_encode_type({:bytes, size}, data, _, _, _) do
|
||||||
raise "wrong datatype for bytes#{size}: #{inspect(data)}"
|
raise "wrong datatype for bytes#{size}: #{inspect(data)}"
|
||||||
end
|
end
|
||||||
|
|
||||||
defp do_encode_type({:array, type}, data, static_acc, dynamic_acc) do
|
defp do_encode_type({:array, type}, data, static_acc, dynamic_acc, mode) do
|
||||||
param_count = Enum.count(data)
|
param_count = Enum.count(data)
|
||||||
|
|
||||||
encoded_size = encode_uint(param_count, 256)
|
|
||||||
|
|
||||||
types = List.duplicate(type, param_count)
|
types = List.duplicate(type, param_count)
|
||||||
|
|
||||||
result = do_encode(data, types)
|
result = do_encode(data, types, mode)
|
||||||
|
|
||||||
dynamic_acc_with_size = [encoded_size | dynamic_acc]
|
{dynamic_acc_with_size, data_bytes_size} =
|
||||||
|
case mode do
|
||||||
|
:standard ->
|
||||||
|
encoded_size = encode_uint(param_count, 256, mode)
|
||||||
|
# length is included and also length size is added
|
||||||
|
{[encoded_size | dynamic_acc], byte_size(result) + 32}
|
||||||
|
|
||||||
# number of elements count + data size
|
:packed ->
|
||||||
data_bytes_size = byte_size(result) + 32
|
# ignoring length of array
|
||||||
|
{dynamic_acc, byte_size(result)}
|
||||||
|
end
|
||||||
|
|
||||||
{[{:dynamic, data_bytes_size} | static_acc], [result | dynamic_acc_with_size]}
|
{[{:dynamic, data_bytes_size} | static_acc], [result | dynamic_acc_with_size]}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp do_encode_type({:array, type, size}, data, static_acc, dynamic_acc) do
|
defp do_encode_type({:array, type, size}, data, static_acc, dynamic_acc, mode) do
|
||||||
types = List.duplicate(type, size)
|
types = List.duplicate(type, size)
|
||||||
result = do_encode(data, types)
|
result = do_encode(data, types, mode)
|
||||||
|
|
||||||
if FunctionSelector.is_dynamic?(type) do
|
if FunctionSelector.is_dynamic?(type) do
|
||||||
data_bytes_size = byte_size(result)
|
data_bytes_size = byte_size(result)
|
||||||
|
@ -169,20 +202,30 @@ defmodule ABI.TypeEncoder do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp do_encode_type(:address, data, static_acc, dynamic_acc) do
|
defp do_encode_type(:address, data, static_acc, dynamic_acc, mode) do
|
||||||
do_encode_type({:uint, 160}, data, static_acc, dynamic_acc)
|
do_encode_type({:uint, 160}, data, static_acc, dynamic_acc, mode)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp do_encode_type(type = {:tuple, _types}, tuple_parameters, static_acc, dynamic_acc)
|
defp do_encode_type({:tuple, _types}, _, _, _, :packed) do
|
||||||
|
raise RuntimeError, "Structs (tuples) are not supported in packed mode encoding"
|
||||||
|
end
|
||||||
|
|
||||||
|
defp do_encode_type(
|
||||||
|
type = {:tuple, _types},
|
||||||
|
tuple_parameters,
|
||||||
|
static_acc,
|
||||||
|
dynamic_acc,
|
||||||
|
:standard
|
||||||
|
)
|
||||||
when is_tuple(tuple_parameters) do
|
when is_tuple(tuple_parameters) do
|
||||||
list_parameters = Tuple.to_list(tuple_parameters)
|
list_parameters = Tuple.to_list(tuple_parameters)
|
||||||
|
|
||||||
do_encode_type(type, list_parameters, static_acc, dynamic_acc)
|
do_encode_type(type, list_parameters, static_acc, dynamic_acc, :standard)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp do_encode_type(type = {:tuple, types}, list_parameters, static_acc, dynamic_acc)
|
defp do_encode_type(type = {:tuple, types}, list_parameters, static_acc, dynamic_acc, :standard)
|
||||||
when is_list(list_parameters) do
|
when is_list(list_parameters) do
|
||||||
result = do_encode(list_parameters, types)
|
result = do_encode(list_parameters, types, :standard)
|
||||||
|
|
||||||
if FunctionSelector.is_dynamic?(type) do
|
if FunctionSelector.is_dynamic?(type) do
|
||||||
data_bytes_size = byte_size(result)
|
data_bytes_size = byte_size(result)
|
||||||
|
@ -193,8 +236,8 @@ defmodule ABI.TypeEncoder do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp encode_bytes(bytes) do
|
defp encode_bytes(bytes, mode) do
|
||||||
pad(bytes, byte_size(bytes), :right)
|
pad(bytes, byte_size(bytes), :right, mode)
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec encode_method_id(FunctionSelector.t()) :: binary()
|
@spec encode_method_id(FunctionSelector.t()) :: binary()
|
||||||
|
@ -216,7 +259,7 @@ defmodule ABI.TypeEncoder do
|
||||||
|
|
||||||
# Note, we'll accept a binary or an integer here, so long as the
|
# Note, we'll accept a binary or an integer here, so long as the
|
||||||
# binary is not longer than our allowed data size
|
# binary is not longer than our allowed data size
|
||||||
defp encode_uint(data, size_in_bits) when rem(size_in_bits, 8) == 0 do
|
defp encode_uint(data, size_in_bits, mode) when rem(size_in_bits, 8) == 0 do
|
||||||
size_in_bytes = (size_in_bits / 8) |> round
|
size_in_bytes = (size_in_bits / 8) |> round
|
||||||
bin = maybe_encode_unsigned(data)
|
bin = maybe_encode_unsigned(data)
|
||||||
|
|
||||||
|
@ -226,22 +269,22 @@ defmodule ABI.TypeEncoder do
|
||||||
"Data overflow encoding uint, data `#{data}` cannot fit in #{size_in_bytes * 8} bits"
|
"Data overflow encoding uint, data `#{data}` cannot fit in #{size_in_bytes * 8} bits"
|
||||||
)
|
)
|
||||||
|
|
||||||
bin |> pad(size_in_bytes, :left)
|
bin |> pad(size_in_bytes, :left, mode)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp encode_int(data, size_in_bits) when rem(size_in_bits, 8) == 0 do
|
defp encode_int(data, size_in_bits, mode) when rem(size_in_bits, 8) == 0 do
|
||||||
if signed_overflow?(data, size_in_bits) do
|
if signed_overflow?(data, size_in_bits) do
|
||||||
raise("Data overflow encoding int, data `#{data}` cannot fit in #{size_in_bits} bits")
|
raise("Data overflow encoding int, data `#{data}` cannot fit in #{size_in_bits} bits")
|
||||||
end
|
end
|
||||||
|
|
||||||
encode_int(data)
|
case mode do
|
||||||
|
:standard -> <<data::signed-256>>
|
||||||
|
:packed -> <<data::signed-size(size_in_bits)>>
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# encoding with integer-signed-256 we already get the right padding
|
|
||||||
defp encode_int(data), do: <<data::signed-256>>
|
|
||||||
|
|
||||||
defp signed_overflow?(n, max_bits) do
|
defp signed_overflow?(n, max_bits) do
|
||||||
n < :math.pow(2, max_bits - 1) * -1 + 1 || n > :math.pow(2, max_bits - 1) - 1
|
n < 2 ** (max_bits - 1) * -1 + 1 || n > 2 ** (max_bits - 1) - 1
|
||||||
end
|
end
|
||||||
|
|
||||||
def mod(x, n) do
|
def mod(x, n) do
|
||||||
|
@ -252,7 +295,19 @@ defmodule ABI.TypeEncoder do
|
||||||
else: remainder
|
else: remainder
|
||||||
end
|
end
|
||||||
|
|
||||||
defp pad(bin, size_in_bytes, direction) do
|
defp pad(bin, size_in_bytes, _direction, :packed) when byte_size(bin) == size_in_bytes, do: bin
|
||||||
|
|
||||||
|
defp pad(bin, size_in_bytes, direction, :packed) when byte_size(bin) < size_in_bytes do
|
||||||
|
padding_size_bits = (size_in_bytes - byte_size(bin)) * 8
|
||||||
|
padding = <<0::size(padding_size_bits)>>
|
||||||
|
|
||||||
|
case direction do
|
||||||
|
:left -> padding <> bin
|
||||||
|
:right -> bin <> padding
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp pad(bin, size_in_bytes, direction, :standard) do
|
||||||
total_size = size_in_bytes + mod(32 - size_in_bytes, 32)
|
total_size = size_in_bytes + mod(32 - size_in_bytes, 32)
|
||||||
padding_size_bits = (total_size - byte_size(bin)) * 8
|
padding_size_bits = (total_size - byte_size(bin)) * 8
|
||||||
padding = <<0::size(padding_size_bits)>>
|
padding = <<0::size(padding_size_bits)>>
|
||||||
|
|
2
mix.exs
2
mix.exs
|
@ -4,7 +4,7 @@ defmodule ABI.Mixfile do
|
||||||
def project do
|
def project do
|
||||||
[
|
[
|
||||||
app: :ex_abi,
|
app: :ex_abi,
|
||||||
version: "0.6.3",
|
version: "0.6.4",
|
||||||
elixir: "~> 1.8",
|
elixir: "~> 1.8",
|
||||||
description: "Ethereum's ABI Interface",
|
description: "Ethereum's ABI Interface",
|
||||||
package: [
|
package: [
|
||||||
|
|
2
mix.lock
2
mix.lock
|
@ -2,7 +2,7 @@
|
||||||
"bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"},
|
"bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"},
|
||||||
"castore": {:hex, :castore, "1.0.4", "ff4d0fb2e6411c0479b1d965a814ea6d00e51eb2f58697446e9c41a97d940b28", [:mix], [], "hexpm", "9418c1b8144e11656f0be99943db4caf04612e3eaecefb5dae9a2a87565584f8"},
|
"castore": {:hex, :castore, "1.0.4", "ff4d0fb2e6411c0479b1d965a814ea6d00e51eb2f58697446e9c41a97d940b28", [:mix], [], "hexpm", "9418c1b8144e11656f0be99943db4caf04612e3eaecefb5dae9a2a87565584f8"},
|
||||||
"credo": {:hex, :credo, "1.7.1", "6e26bbcc9e22eefbff7e43188e69924e78818e2fe6282487d0703652bc20fd62", [:mix], [{:bunt, "~> 0.2.1", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "e9871c6095a4c0381c89b6aa98bc6260a8ba6addccf7f6a53da8849c748a58a2"},
|
"credo": {:hex, :credo, "1.7.1", "6e26bbcc9e22eefbff7e43188e69924e78818e2fe6282487d0703652bc20fd62", [:mix], [{:bunt, "~> 0.2.1", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "e9871c6095a4c0381c89b6aa98bc6260a8ba6addccf7f6a53da8849c748a58a2"},
|
||||||
"dialyxir": {:hex, :dialyxir, "1.4.1", "a22ed1e7bd3a3e3f197b68d806ef66acb61ee8f57b3ac85fc5d57354c5482a93", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "84b795d6d7796297cca5a3118444b80c7d94f7ce247d49886e7c291e1ae49801"},
|
"dialyxir": {:hex, :dialyxir, "1.4.2", "764a6e8e7a354f0ba95d58418178d486065ead1f69ad89782817c296d0d746a5", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "516603d8067b2fd585319e4b13d3674ad4f314a5902ba8130cd97dc902ce6bbd"},
|
||||||
"earmark_parser": {:hex, :earmark_parser, "1.4.37", "2ad73550e27c8946648b06905a57e4d454e4d7229c2dafa72a0348c99d8be5f7", [:mix], [], "hexpm", "6b19783f2802f039806f375610faa22da130b8edc21209d0bff47918bb48360e"},
|
"earmark_parser": {:hex, :earmark_parser, "1.4.37", "2ad73550e27c8946648b06905a57e4d454e4d7229c2dafa72a0348c99d8be5f7", [:mix], [], "hexpm", "6b19783f2802f039806f375610faa22da130b8edc21209d0bff47918bb48360e"},
|
||||||
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
|
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
|
||||||
"ex_doc": {:hex, :ex_doc, "0.30.9", "d691453495c47434c0f2052b08dd91cc32bc4e1a218f86884563448ee2502dd2", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "d7aaaf21e95dc5cddabf89063327e96867d00013963eadf2c6ad135506a8bc10"},
|
"ex_doc": {:hex, :ex_doc, "0.30.9", "d691453495c47434c0f2052b08dd91cc32bc4e1a218f86884563448ee2502dd2", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "d7aaaf21e95dc5cddabf89063327e96867d00013963eadf2c6ad135506a8bc10"},
|
||||||
|
|
|
@ -35,7 +35,7 @@ defmodule ABI.TypeEncoderTest do
|
||||||
assert TypeDecoder.decode(expected_result, selector) == params
|
assert TypeDecoder.decode(expected_result, selector) == params
|
||||||
end
|
end
|
||||||
|
|
||||||
test "encodes [{:int, 25}, :bool]" do
|
test "encodes [{:int, 256}, :bool]" do
|
||||||
selector = %FunctionSelector{
|
selector = %FunctionSelector{
|
||||||
function: "baz",
|
function: "baz",
|
||||||
method_id: <<215, 174, 202, 43>>,
|
method_id: <<215, 174, 202, 43>>,
|
||||||
|
@ -408,6 +408,67 @@ defmodule ABI.TypeEncoderTest do
|
||||||
|
|
||||||
assert ABI.TypeDecoder.decode(expected_result, selector) == params
|
assert ABI.TypeDecoder.decode(expected_result, selector) == params
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "encodes bytes without padding in packed mode" do
|
||||||
|
assert <<0, 1, 2>> == ABI.TypeEncoder.encode([<<0, 1, 2>>], [:bytes], :input, :packed)
|
||||||
|
assert "Hello" == ABI.TypeEncoder.encode(["Hello"], [:string], :input, :packed)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "encodes types smaller than 256 bits without padding in packed mode" do
|
||||||
|
for size <- 8..256//8 do
|
||||||
|
value = Enum.random(1..255)
|
||||||
|
|
||||||
|
assert <<value::unsigned-size(size)>> ==
|
||||||
|
ABI.TypeEncoder.encode([value], [{:uint, size}], :input, :packed)
|
||||||
|
|
||||||
|
value = Enum.random(-128..127)
|
||||||
|
|
||||||
|
assert <<value::signed-size(size)>> ==
|
||||||
|
ABI.TypeEncoder.encode([value], [{:int, size}], :input, :packed)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "encodes bool as one byte in packed mode" do
|
||||||
|
assert <<1>> == ABI.TypeEncoder.encode([true], [:bool], :input, :packed)
|
||||||
|
assert <<0>> == ABI.TypeEncoder.encode([false], [:bool], :input, :packed)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "encodes arrays with padding in packed mode" do
|
||||||
|
assert ABI.TypeEncoder.encode([[1, 2, 3]], [{:array, {:uint, 8}}]) ==
|
||||||
|
"00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003"
|
||||||
|
|> Base.decode16!(case: :lower)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "encodes address in 20 bytes in packed mode" do
|
||||||
|
encoded =
|
||||||
|
ABI.TypeEncoder.encode(
|
||||||
|
[
|
||||||
|
<<192, 42, 170, 57, 178, 35, 254, 141, 10, 14, 92, 79, 39, 234, 217, 8, 60, 117, 108,
|
||||||
|
194>>
|
||||||
|
],
|
||||||
|
[:address],
|
||||||
|
:input,
|
||||||
|
:packed
|
||||||
|
)
|
||||||
|
|
||||||
|
assert byte_size(encoded) == 20
|
||||||
|
end
|
||||||
|
|
||||||
|
test "raises with tuple in packed mode" do
|
||||||
|
data_to_encode = [{128, 64}]
|
||||||
|
|
||||||
|
selector = %FunctionSelector{
|
||||||
|
function: "baz",
|
||||||
|
types: [
|
||||||
|
{:tuple, [{:int, 8}, {:int, 8}]}
|
||||||
|
],
|
||||||
|
returns: :bool
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_raise RuntimeError, fn ->
|
||||||
|
ABI.TypeEncoder.encode(data_to_encode, selector, :input, :packed)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "example 1 from web3-eth-abi js" do
|
test "example 1 from web3-eth-abi js" do
|
||||||
|
|
Loading…
Reference in New Issue