Skip to content

Commit

Permalink
get_abi_input_* and get_abi_output_* public utils
Browse files Browse the repository at this point in the history
  • Loading branch information
reedsa committed May 9, 2024
1 parent 7fa632b commit 6bd10a3
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 27 deletions.
4 changes: 2 additions & 2 deletions docs/web3.contract.rst
Original file line number Diff line number Diff line change
Expand Up @@ -634,7 +634,7 @@ Taking the following contract code as an example:
>>> arrays_contract.functions.setBytes2Value([b'b']).transact()
Traceback (most recent call last):
...
web3.exceptions.Web3ValidationError:
web3.exceptions.Web3TypeError:
Could not identify the intended function with name
>>> # check value is still b'aa'
>>> arrays_contract.functions.getBytes2Value().call()
Expand All @@ -659,7 +659,7 @@ Taking the following contract code as an example:
>>> arrays_contract.functions.setBytes2Value([b'a']).transact()
Traceback (most recent call last):
...
web3.exceptions.Web3ValidationError:
web3.exceptions.Web3TypeError:
Could not identify the intended function with name

.. _contract-functions:
Expand Down
8 changes: 6 additions & 2 deletions docs/web3.utils.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@ The ``utils`` module houses public utility functions and classes.
ABI
---

ABI utilities re-exported as ``web3.utils.abi`` from
`eth-utils <https://github.com/ethereum/eth-utils>`_.
``web3.utils.abi``

Application Binary Interface (ABI) methods to encode/decode contract transactions.

It is generally recommended to use the `web3.eth.contract` module. These methods are
useful when a contract's ABI is known and a Web3 instance is not needed.

.. automodule:: web3.utils.abi
:members:
Expand Down
11 changes: 9 additions & 2 deletions ens/base_ens.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@

from .utils import (
address_to_reverse_domain,
get_abi_output_types,
is_valid_name,
label_to_hash,
normalize_name,
raw_name_to_hash,
get_abi_output_types,
)

if TYPE_CHECKING:
Expand Down Expand Up @@ -108,7 +108,14 @@ def _decode_ensip10_resolve_data(
if fn_name == "addr"
else extended_resolver.get_function_by_name(fn_name)
)
output_types = get_abi_output_types(func.abi)
try:
output_types = get_abi_output_types(func.abi)
except ValueError:
# constructor, fallback and receive functions do not have outputs
# proceed with decoding data without outputs
output_types = []
pass

decoded = self.w3.codec.decode(output_types, contract_call_result)

# if decoding a single value, return that value - else, return the tuple
Expand Down
12 changes: 3 additions & 9 deletions ens/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,7 @@
remove_0x_prefix,
to_bytes,
to_normalized_address,
)
from eth_utils.abi import (
get_normalized_abi_arg_type,
get_abi_output_types as abi_output_types,
)
from hexbytes import (
HexBytes,
Expand Down Expand Up @@ -293,13 +291,9 @@ def is_valid_ens_name(ens_name: str) -> bool:
return True


# borrowed from similar method at `web3._utils.abi` due to circular dependency
# Wrap `get_abi_output_types` to prevent direct imports causing circular dependencies
def get_abi_output_types(abi: "ABIFunction") -> List[str]:
return (
[]
if abi["type"] == "fallback"
else [get_normalized_abi_arg_type(arg) for arg in abi["outputs"]]
)
return abi_output_types(abi)


# -- async -- #
Expand Down
19 changes: 13 additions & 6 deletions web3/_utils/abi.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,12 +158,19 @@ def filter_by_argument_count(
def filter_by_argument_name(
argument_names: Collection[str], contract_abi: ABI
) -> List[Union[ABIFunction, ABIEvent]]:
return [
abi
for abi in contract_abi
if set(argument_names).intersection(get_abi_input_names(abi))
== set(argument_names)
]
abis_with_matching_args = ()
for abi_element in contract_abi:
try:
abi_arg_names = get_abi_input_names(abi_element)

if set(argument_names).intersection(abi_arg_names) == set(abi_arg_names):
abis_with_matching_args.push(abi_element)
except ValueError:
# fallback or receive functions do not have arguments
# proceed to next ABIElement
continue

return abis_with_matching_args


# type ignored because subclassing encoding.AddressEncoder which has type Any
Expand Down
21 changes: 17 additions & 4 deletions web3/_utils/contracts.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,12 @@ def encode_abi(
arguments: Sequence[Any],
data: Optional[HexStr] = None,
) -> HexStr:
argument_types = get_abi_input_types(abi)
try:
argument_types = get_abi_input_types(abi)
except ValueError:
# fallback or receive functions do not have arguments
# return encoded data without arguments
return to_hex(HexBytes(data))

if not check_if_arguments_can_be_encoded(abi, w3.codec, arguments, {}):
raise Web3TypeError(
Expand Down Expand Up @@ -332,11 +337,19 @@ def decode_transaction_data(
normalizers: Sequence[Callable[[TypeStr, Any], Tuple[TypeStr, Any]]] = None,
) -> Dict[str, Any]:
data = HexBytes(data)
types = get_abi_input_types(fn_abi)

try:
argument_types = get_abi_input_types(fn_abi)
except ValueError:
# fallback or receive functions do not have arguments
# proceed with decoding data without arguments
argument_types = []
pass

abi_codec = ABICodec(default_registry)
decoded = abi_codec.decode(types, HexBytes(data[4:]))
decoded = abi_codec.decode(argument_types, HexBytes(data[4:]))
if normalizers:
decoded = map_abi_data(normalizers, types, decoded)
decoded = map_abi_data(normalizers, argument_types, decoded)
return named_tree(fn_abi["inputs"], decoded)


Expand Down
16 changes: 14 additions & 2 deletions web3/contract/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,13 @@ def call_contract_function(
contract_abi, w3.codec, function_identifier, args, kwargs
)

output_types = get_abi_output_types(fn_abi)
try:
output_types = get_abi_output_types(fn_abi)
except ValueError:
# constructor, fallback and receive functions do not have arguments
# proceed with decoding data without arguments
output_types = []
pass

try:
output_data = w3.codec.decode(output_types, return_data)
Expand Down Expand Up @@ -319,7 +325,13 @@ async def async_call_contract_function(
contract_abi, async_w3.codec, function_identifier, args, kwargs
)

output_types = get_abi_output_types(fn_abi)
try:
output_types = get_abi_output_types(fn_abi)
except ValueError:
# constructor, fallback and receive functions do not have arguments
# proceed with decoding data without arguments
output_types = []
pass

try:
output_data = async_w3.codec.decode(output_types, return_data)
Expand Down

0 comments on commit 6bd10a3

Please sign in to comment.