mirror of https://github.com/poanetwork/ex_abi.git
Compare commits
2 Commits
5d63e8a7fe
...
67eb2b9d5d
Author | SHA1 | Date |
---|---|---|
Fedor Ivanov | 67eb2b9d5d | |
Fedor Ivanov | e3dc19b814 |
|
@ -180,7 +180,31 @@ defmodule ABI.FunctionSelector do
|
|||
"name" => function_name,
|
||||
"inputs" => named_inputs,
|
||||
"outputs" => named_outputs
|
||||
} <- item,
|
||||
} <-
|
||||
item
|
||||
# Workaround for ABIs that are missing the "outputs" field.
|
||||
#
|
||||
# For instance, consider this valid ABI:
|
||||
#
|
||||
# ```jsonc
|
||||
# // ...
|
||||
# {
|
||||
# "type": "function",
|
||||
# "stateMutability": "view",
|
||||
# "name": "assumeLastTokenIdMatches",
|
||||
# "inputs": [
|
||||
# {
|
||||
# "type": "uint256",
|
||||
# "name": "lastTokenId",
|
||||
# "internalType": "uint256"
|
||||
# }
|
||||
# ]
|
||||
# },
|
||||
# // ...
|
||||
# ```
|
||||
# If the "outputs" field is missing, we should assume it's an empty
|
||||
# list to continue parsing the ABI.
|
||||
|> Map.put_new("outputs", []),
|
||||
true <- simple_types?(named_inputs, item),
|
||||
true <- simple_types?(named_outputs, item) do
|
||||
input_types = Enum.map(named_inputs, &parse_specification_type/1)
|
||||
|
@ -311,34 +335,28 @@ defmodule ABI.FunctionSelector do
|
|||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
def parse_specification_type(%{"type" => "tuple", "components" => components}) do
|
||||
sub_types = for component <- components, do: parse_specification_type(component)
|
||||
defp replace_tuple({:array, inner}, sub_types) do
|
||||
{:array, replace_tuple(inner, sub_types)}
|
||||
end
|
||||
|
||||
defp replace_tuple({:array, inner, size}, sub_types) do
|
||||
{:array, replace_tuple(inner, sub_types), size}
|
||||
end
|
||||
|
||||
defp replace_tuple(:tuple, sub_types) do
|
||||
{:tuple, sub_types}
|
||||
end
|
||||
|
||||
def parse_specification_type(%{"type" => "tuple[]", "components" => components}) do
|
||||
sub_types = for component <- components, do: parse_specification_type(component)
|
||||
{:array, {:tuple, sub_types}}
|
||||
defp replace_tuple(other, _) do
|
||||
other
|
||||
end
|
||||
|
||||
def parse_specification_type(%{"type" => "tuple[][]", "components" => components}) do
|
||||
sub_types = for component <- components, do: parse_specification_type(component)
|
||||
{:array, {:array, {:tuple, sub_types}}}
|
||||
end
|
||||
|
||||
def parse_specification_type(%{
|
||||
"type" => "tuple[" <> tail,
|
||||
"components" => components
|
||||
}) do
|
||||
def parse_specification_type(%{"type" => "tuple" <> _ = type, "components" => components}) do
|
||||
sub_types = for component <- components, do: parse_specification_type(component)
|
||||
|
||||
size =
|
||||
tail
|
||||
|> String.replace("]", "")
|
||||
|> String.to_integer()
|
||||
|
||||
{:array, {:tuple, sub_types}, size}
|
||||
type
|
||||
|> decode_type()
|
||||
|> replace_tuple(sub_types)
|
||||
end
|
||||
|
||||
def parse_specification_type(%{"type" => type}), do: decode_type(type)
|
||||
|
|
|
@ -111,6 +111,43 @@ defmodule ABI.FunctionSelectorTest do
|
|||
}
|
||||
] == ABI.parse_specification([abi])
|
||||
end
|
||||
|
||||
@doc """
|
||||
Regression test to verify the correct parsing of ABIs that lack the
|
||||
"outputs" field.
|
||||
"""
|
||||
test "with the missing outputs field" do
|
||||
abi = [
|
||||
%{
|
||||
"type" => "function",
|
||||
"stateMutability" => "view",
|
||||
"name" => "assumeLastTokenIdMatches",
|
||||
"inputs" => [
|
||||
%{
|
||||
"type" => "uint256",
|
||||
"name" => "lastTokenId",
|
||||
"internalType" => "uint256"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
expected = [
|
||||
%FunctionSelector{
|
||||
type: :function,
|
||||
function: "assumeLastTokenIdMatches",
|
||||
input_names: ["lastTokenId"],
|
||||
types: [uint: 256],
|
||||
returns: [],
|
||||
return_names: [],
|
||||
method_id: <<231, 40, 120, 180>>,
|
||||
inputs_indexed: nil,
|
||||
state_mutability: :view
|
||||
}
|
||||
]
|
||||
|
||||
assert ABI.parse_specification(abi) == expected
|
||||
end
|
||||
end
|
||||
|
||||
describe "parse_specification_item/1" do
|
||||
|
@ -345,6 +382,38 @@ defmodule ABI.FunctionSelectorTest do
|
|||
assert expected_type == selector.types
|
||||
end
|
||||
|
||||
test "parses fixed 2D array of tuples" do
|
||||
function = %{
|
||||
"inputs" => [],
|
||||
"name" => "createTupleArray",
|
||||
"outputs" => [
|
||||
%{
|
||||
"components" => [
|
||||
%{
|
||||
"internalType" => "uint256",
|
||||
"name" => "element1",
|
||||
"type" => "uint256"
|
||||
},
|
||||
%{"internalType" => "bool", "name" => "element2", "type" => "bool"}
|
||||
],
|
||||
"internalType" => "struct StorageB.MyTuple[2][]",
|
||||
"name" => "",
|
||||
"type" => "tuple[2][]"
|
||||
}
|
||||
],
|
||||
"stateMutability" => "pure",
|
||||
"type" => "function"
|
||||
}
|
||||
|
||||
expected = [
|
||||
array: {:array, {:tuple, [{:uint, 256}, :bool]}, 2}
|
||||
]
|
||||
|
||||
selector = FunctionSelector.parse_specification_item(function)
|
||||
|
||||
assert expected == selector.returns
|
||||
end
|
||||
|
||||
test "with stateMutability set" do
|
||||
~w(pure view nonpayable payable)
|
||||
|> Enum.zip(~w(pure view non_payable payable)a)
|
||||
|
|
Loading…
Reference in New Issue