Skip to content

Commit

Permalink
catch mismatched abi data and test for namedtuple rename
Browse files Browse the repository at this point in the history
  • Loading branch information
pacrob committed Jan 31, 2023
1 parent 619f6db commit faa5801
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 45 deletions.
102 changes: 60 additions & 42 deletions tests/core/utilities/test_abi_named_tree.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import pytest

from eth_abi.codec import (
ABICodec,
)
Expand All @@ -11,70 +13,86 @@
foldable_namedtuple,
named_tree,
)
from web3.exceptions import (
MismatchedABI,
)

from .test_abi import (
TEST_FUNCTION_ABI,
)

abi = TEST_FUNCTION_ABI["inputs"]
inputs = (
full_abi_inputs = TEST_FUNCTION_ABI["inputs"]
full_values = (
(1, [2, 3, 4], [(5, 6), (7, 8), (9, 10)]), # Value for s
(11, 12), # Value for t
13, # Value for a
[[(14, 15), (16, 17)], [(18, 19)]], # Value for b
)

sample_abi_inputs = {
"inputs": [
{
"components": [
{"name": "a", "type": "uint256"},
{"name": "b", "type": "uint256[]"},
{
"components": [
{"name": "x", "type": "uint256"},
{"name": "y", "type": "uint256"},
],
"name": "c",
"type": "tuple[]",
},
],
"name": "s",
"type": "tuple",
},
{
"components": [
{"name": "x", "type": "uint256"},
{"name": "y", "type": "uint256"},
],
"name": "t",
"type": "tuple",
},
{"name": "a", "type": "uint256"},
{
"components": [
{"name": "x", "type": "uint256"},
{"name": "y", "type": "uint256"},
],
"name": "b",
"type": "tuple[][]",
},
]
}

def test_named_arguments_decode():
decoded = named_tree(abi, inputs)
decoded = named_tree(full_abi_inputs, full_values)
data = dict_to_namedtuple(decoded)
assert data == inputs
assert data == full_values
assert data.s.c[2].y == 10
assert data.t.x == 11
assert data.a == 13


short_abi_inputs_with_disallowed_names = [
{
"components": [
{"name": "from", "type": "uint256"},
{"name": "to", "type": "uint256[]"},
{
"components": [
{"name": "_x", "type": "uint256"},
{"name": "_y", "type": "uint256"},
],
"name": "c",
"type": "tuple[]",
},
],
"name": "s",
"type": "tuple",
},
]

short_values = ((1, [2, 3, 4], [(5, 6), (7, 8), (9, 10)]),)


def test_named_arguments_decode_rename():
decoded = named_tree(short_abi_inputs_with_disallowed_names, short_values)
data = dict_to_namedtuple(decoded)
assert data == short_values
assert data._fields == ("s",)

# python keyword "from" renamed to "_0"
assert data.s._fields == ("_0", "to", "c")

# field names starting with "_" - "_x" and "_y" - renamed to "_0" and "_1"
assert data.s.c[0]._fields == ("_0", "_1")
assert data.s.c[2]._1 == 10
assert data.s.to[1] == 3


@pytest.mark.parametrize(
"values",
(
((1, [2, 3, 4], [(5,), (7, 8), (9, 10)]),),
((1, [2, 3, 4], [(5, 6, 11), (7, 8), (9, 10)]),),
((1, [(5, 6), (7, 8), (9, 10)]),),
),
)
def test_named_arguments_decode_with_misshapen_inputs(values):
with pytest.raises(MismatchedABI):
named_tree(short_abi_inputs_with_disallowed_names, values)


def test_namedtuples_encodable():
registry = default_registry.copy()
codec = ABICodec(registry)
kwargs = named_tree(abi, inputs)
kwargs = named_tree(full_abi_inputs, full_values)
args = dict_to_namedtuple(kwargs)
assert check_if_arguments_can_be_encoded(TEST_FUNCTION_ABI, codec, (), kwargs)
assert check_if_arguments_can_be_encoded(TEST_FUNCTION_ABI, codec, args, {})
Expand Down
13 changes: 10 additions & 3 deletions web3/_utils/abi.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
)
from web3.exceptions import (
FallbackNotFound,
MismatchedABI,
)
from web3.types import (
ABI,
Expand Down Expand Up @@ -920,6 +921,7 @@ def named_tree(

# TODO how to handle if names and items end up different len
# return dict(zip(names, items)) if all(names) else items

return dict(zip(names, items))


Expand All @@ -933,15 +935,20 @@ def named_subtree(
if abi_type.is_array:
item_type = abi_type.item_type.to_type_str()
item_abi = {**abi, "type": item_type, "name": ""}
# cast(ABIFunctionParams, item_abi)
# breakpoint()
items = [named_subtree(item_abi, item) for item in data]
return items

if isinstance(abi_type, TupleType):
names = [item["name"] for item in abi["components"]]
items = [named_subtree(*item) for item in zip(abi["components"], data)]
return dict(zip(names, items))

if len(names) == len(data):
return dict(zip(names, items))
else:
raise MismatchedABI(
f"ABI fields {names} has length {len(names)} but received "
f"data {data} with length {len(data)}"
)

return data

Expand Down

0 comments on commit faa5801

Please sign in to comment.