Skip to content

Commit

Permalink
Merge pull request #51 from fjarri/positional-args
Browse files Browse the repository at this point in the history
Process unnamed arguments in JSON entries correctly
  • Loading branch information
fjarri authored Sep 25, 2023
2 parents 11858e6 + cf6cd3e commit 4a1db2a
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 3 deletions.
12 changes: 12 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@ Changelog
---------


0.7.1 (unreleased)
~~~~~~~~~~~~~~~~~~

Fixed
^^^^^

- Process unnamed arguments in JSON entries correctly (as positional arguments). (PR_51_)


.. _PR_51: https://github.com/fjarri/pons/pull/51


0.7.0 (09-07-2023)
~~~~~~~~~~~~~~~~~~

Expand Down
12 changes: 10 additions & 2 deletions pons/_abi_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -511,9 +511,17 @@ def dispatch_type(abi_entry: Mapping[str, Any]) -> Type:
return type_from_abi_string(element_type_name)


def dispatch_types(abi_entry: Iterable[Dict[str, Any]]) -> Dict[str, Type]:
# Since we are returning a dictionary, need to be sure we don't silently merge entries
def dispatch_types(abi_entry: Iterable[Dict[str, Any]]) -> Union[List[Type], Dict[str, Type]]:
names = [entry["name"] for entry in abi_entry]

# Unnamed arguments; treat as positional arguments
if names and all(not name for name in names):
return [dispatch_type(entry) for entry in abi_entry]

if any(not name for name in names):
raise ValueError("Arguments must be either all named or all unnamed")

# Since we are returning a dictionary, need to be sure we don't silently merge entries
if len(names) != len(set(names)):
raise ValueError("All ABI entries must have distinct names")
return {entry["name"]: dispatch_type(entry) for entry in abi_entry}
Expand Down
5 changes: 5 additions & 0 deletions pons/_contract_abi.py
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,9 @@ def from_json(cls, event_entry: Dict[str, Any]) -> "Event":

name = event_entry["name"]
fields = dispatch_types(event_entry["inputs"])
if isinstance(fields, list):
raise ValueError("Event fields must be named")

indexed = {input_["name"] for input_ in event_entry["inputs"] if input_["indexed"]}

return cls(name=name, fields=fields, indexed=indexed, anonymous=event_entry["anonymous"])
Expand Down Expand Up @@ -530,6 +533,8 @@ def from_json(cls, error_entry: Dict[str, Any]) -> "Error":

name = error_entry["name"]
fields = dispatch_types(error_entry["inputs"])
if isinstance(fields, list):
raise ValueError("Error fields must be named")

return cls(name=name, fields=fields)

Expand Down
17 changes: 16 additions & 1 deletion tests/test_abi_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,14 +212,29 @@ def test_dispatch_types():
dict(name="param2", type="uint8"),
dict(name="param1", type="uint16[2]"),
]

assert dispatch_types(entries) == dict(param2=abi.uint(8), param1=abi.uint(16)[2])

# Check that the order is preserved, too
assert list(dispatch_types(entries).items()) == [
("param2", abi.uint(8)),
("param1", abi.uint(16)[2]),
]

# Note that if all the names are empty, it is treated as a list of positional arguments
assert dispatch_types([dict(name="", type="uint8"), dict(name="", type="uint16[2]")]) == [
abi.uint(8),
abi.uint(16)[2],
]

# For an empty argument list we choose to resolve it as an empty dictionary, for certainty.
assert dispatch_types([]) == {}

with pytest.raises(ValueError, match="Arguments must be either all named or all unnamed"):
dispatch_types([dict(name="foo", type="uint8"), dict(name="", type="uint16[2]")])

with pytest.raises(ValueError, match="All ABI entries must have distinct names"):
dispatch_types([dict(name="", type="uint8"), dict(name="", type="uint16[2]")])
dispatch_types([dict(name="foo", type="uint8"), dict(name="foo", type="uint16[2]")])


def test_making_arrays():
Expand Down
25 changes: 25 additions & 0 deletions tests/test_contract_abi.py
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,19 @@ def test_event_errors():
# This works
Event("Foo", dict(a=uint8, b=uint8, c=uint8, d=uint8, e=uint8), indexed={"a", "b", "c"})

with pytest.raises(ValueError, match="Event fields must be named"):
Event.from_json(
dict(
anonymous=True,
inputs=[
dict(indexed=True, internalType="address", name="", type="address"),
dict(indexed=False, internalType="uint8", name="", type="uint8"),
],
name="Foo",
type="event",
)
)


def test_error_from_json():
error = Error.from_json(
Expand All @@ -622,6 +635,18 @@ def test_error_from_json():
):
Error.from_json(dict(type="constructor"))

with pytest.raises(ValueError, match="Error fields must be named"):
Error.from_json(
dict(
inputs=[
dict(internalType="address", name="", type="address"),
dict(internalType="bytes", name="", type="bytes"),
],
name="Foo",
type="error",
)
)


def test_error_init():
error = Error(
Expand Down

0 comments on commit 4a1db2a

Please sign in to comment.