Beispiel #1
0
def merge_args_and_kwargs(function_abi: ABIFunction, args: Sequence[Any],
                          kwargs: Dict[str, Any]) -> Tuple[Any, ...]:
    """
    Takes a list of positional args (``args``) and a dict of keyword args
    (``kwargs``) defining values to be passed to a call to the contract function
    described by ``function_abi``.  Checks to ensure that the correct number of
    args were given, no duplicate args were given, and no unknown args were
    given.  Returns a list of argument values aligned to the order of inputs
    defined in ``function_abi``.
    """
    # Ensure the function is being applied to the correct number of args
    if len(args) + len(kwargs) != len(function_abi.get('inputs', [])):
        raise TypeError(
            f"Incorrect argument count. Expected '{len(function_abi['inputs'])}"
            f". Got '{len(args) + len(kwargs)}'")

    # If no keyword args were given, we don't need to align them
    if not kwargs:
        return cast(Tuple[Any, ...], args)

    kwarg_names = set(kwargs.keys())
    sorted_arg_names = tuple(arg_abi['name']
                             for arg_abi in function_abi['inputs'])
    args_as_kwargs = dict(zip(sorted_arg_names, args))

    # Check for duplicate args
    duplicate_args = kwarg_names.intersection(args_as_kwargs.keys())
    if duplicate_args:
        raise TypeError(
            f"{function_abi.get('name')}() got multiple values for argument(s) "
            f"'{', '.join(duplicate_args)}'")

    # Check for unknown args
    unknown_args = kwarg_names.difference(sorted_arg_names)
    if unknown_args:
        if function_abi.get('name'):
            raise TypeError(
                f"{function_abi.get('name')}() got unexpected keyword argument(s)"
                f" '{', '.join(unknown_args)}'")
        raise TypeError(
            f"Type: '{function_abi.get('type')}' got unexpected keyword argument(s)"
            f" '{', '.join(unknown_args)}'")

    # Sort args according to their position in the ABI and unzip them from their
    # names
    sorted_args = tuple(
        zip(*sorted(
            itertools.chain(kwargs.items(), args_as_kwargs.items()),
            key=lambda kv: sorted_arg_names.index(kv[0]),
        )))

    if sorted_args:
        return sorted_args[1]
    else:
        return tuple()
Beispiel #2
0
def get_aligned_abi_inputs(
    abi: ABIFunction, args: Union[Tuple[Any, ...], Mapping[Any, Any]]
) -> Tuple[Tuple[Any, ...], Tuple[Any, ...]]:
    """
    Takes a function ABI (``abi``) and a sequence or mapping of args (``args``).
    Returns a list of type strings for the function's inputs and a list of
    arguments which have been aligned to the layout of those types.  The args
    contained in ``args`` may contain nested mappings or sequences corresponding
    to tuple-encoded values in ``abi``.
    """
    input_abis = abi.get('inputs', [])

    if isinstance(args, abc.Mapping):
        # `args` is mapping.  Align values according to abi order.
        args = tuple(args[abi['name']] for abi in input_abis)

    return (
        # typed dict cannot be used w/ a normal Dict
        # https://github.com/python/mypy/issues/4976
        tuple(collapse_if_tuple(abi) for abi in input_abis),  # type: ignore
        type(args)(
            _align_abi_input(abi, arg)
            for abi, arg in zip(input_abis, args)
        ),
    )
Beispiel #3
0
def check_if_arguments_can_be_encoded(
    function_abi: ABIFunction,
    abi_codec: codec.ABIEncoder,
    args: Sequence[Any],
    kwargs: Dict[str, Any],
) -> bool:
    try:
        arguments = merge_args_and_kwargs(function_abi, args, kwargs)
    except TypeError:
        return False

    if len(function_abi.get('inputs', [])) != len(arguments):
        return False

    try:
        types, aligned_args = get_aligned_abi_inputs(function_abi, arguments)
    except TypeError:
        return False

    return all(
        abi_codec.is_encodable(_type, arg)
        for _type, arg in zip(types, aligned_args))