tronpytool.common.contracts API documentation

# Module `tronpytool.common.contracts`

Expand source code

    # --------------------------------------------------------------------
    # Copyright (c) iEXBase. All rights reserved.
    # Licensed under the MIT License.
    # See License.txt in the project root for license information.
    # --------------------------------------------------------------------

    import functools
    import re
    from typing import Union

    from eth_abi import (
        encode_abi as eth_abi_encode_abi,
    )
    from eth_abi.exceptions import (
        EncodingError,
    )
    from eth_utils import (
        to_hex,
        function_abi_to_4byte_selector
    )
    from hexbytes import HexBytes
    from trx_utils import is_text, encode_hex
    from trx_utils.typing import HexStr

    from tronpytool.common.abi import (
        filter_by_name,
        filter_by_encodability,
        filter_by_argument_count,
        get_fallback_func_abi,
        abi_to_signature,
        get_abi_input_types,
        check_if_arguments_can_be_encoded,
        map_abi_data,
        merge_args_and_kwargs
    )
    from tronpytool.common.normalizers import (
        abi_address_to_hex,
        abi_bytes_to_bytes,
        abi_string_to_text
    )
    from tronpytool.common.toolz import (
        pipe,
        valmap,
    )

    class FallbackFn:
        pass

    def find_match_tron_address(test_str: str) -> str:
        regex = r"\T.[a-zA-Z0-9]{32}"

        matches = re.search(regex, test_str)

        if matches:
            print("Match was found at {start}-{end}: {match}".format(start=matches.start(), end=matches.end(),
                                                                     match=matches.group()))

            for groupNum in range(0, len(matches.groups())):
                groupNum = groupNum + 1

                print("Group {groupNum} found at {start}-{end}: {group}".format(groupNum=groupNum,
                                                                                start=matches.start(groupNum),
                                                                                end=matches.end(groupNum),
                                                                                group=matches.group(groupNum)))

        return matches

    def find_matching_fn_abi(abi, fn_identifier=None, args=None, kwargs=None):
        args = args or tuple()
        kwargs = kwargs or dict()
        filters = []
        num_arguments = len(args) + len(kwargs)
        diagnosis = None

        if fn_identifier is FallbackFn:
            return get_fallback_func_abi(abi)

        if not is_text(fn_identifier):
            raise TypeError("Unsupported function identifier")

        name_filter = functools.partial(filter_by_name, fn_identifier)
        arg_count_filter = functools.partial(filter_by_argument_count, num_arguments)
        encoding_filter = functools.partial(filter_by_encodability, args, kwargs)
        filters.extend([
            name_filter,
            arg_count_filter,
            encoding_filter,
        ])
        function_candidates = pipe(abi, *filters)

        if len(function_candidates) == 1:
            return function_candidates[0]
        else:
            matching_identifiers = name_filter(abi)
            matching_function_signatures = [abi_to_signature(func) for func in matching_identifiers]

            arg_count_matches = len(arg_count_filter(matching_identifiers))
            encoding_matches = len(encoding_filter(matching_identifiers))

            if arg_count_matches == 0:
                diagnosis = "\nFunction invocation failed due to improper number of arguments."
            elif encoding_matches == 0:
                diagnosis = "\nFunction invocation failed due to no matching argument types."
            elif encoding_matches > 1:
                diagnosis = (
                    "\nAmbiguous argument encoding. "
                    "Provided arguments can be encoded to multiple functions matching this call."
                )
            message = (
                "\nCould not identify the intended function with name `{name}`, "
                "positional argument(s) of type `{arg_types}` and "
                "keyword argument(s) of type `{kwarg_types}`."
                "\nFound {num_candidates} function(s) with the name `{name}`: {candidates}"
                "{diagnosis}"
            ).format(
                name=fn_identifier,
                arg_types=tuple(map(type, args)),
                kwarg_types=valmap(type, kwargs),
                num_candidates=len(matching_identifiers),
                candidates=matching_function_signatures,
                diagnosis=diagnosis,
            )
            raise ValueError(message)

    def encode_abi(tron, abi, arguments, data=None) -> Union[HexStr, str]:
        argument_types = get_abi_input_types(abi)

        if not check_if_arguments_can_be_encoded(abi, arguments, {}):
            raise TypeError(
                "One or more arguments could not be encoded to the necessary "
                "ABI type.  Expected types are: {0}".format(
                    ', '.join(argument_types),
                )
            )

        try:
            normalizers = [
                abi_address_to_hex,
                abi_bytes_to_bytes,
                abi_string_to_text,
            ]

            normalized_arguments = map_abi_data(
                normalizers,
                argument_types,
                arguments,
            )

            encoded_arguments = eth_abi_encode_abi(
                argument_types,
                normalized_arguments,
            )
        except EncodingError as e:
            raise TypeError(
                "One or more arguments could not be encoded to the necessary "
                "ABI type: {0}".format(str(e))
            )

        if data:
            return to_hex(HexBytes(data) + encoded_arguments)
        else:
            return encode_hex(encoded_arguments)

    def get_function_info(fn_name, contract_abi=None, fn_abi=None, args=None, kwargs=None):
        if args is None:
            args = tuple()
        if kwargs is None:
            kwargs = {}

        if fn_abi is None:
            fn_abi = find_matching_fn_abi(contract_abi, fn_name, args, kwargs)

        fn_selector = encode_hex(function_abi_to_4byte_selector(fn_abi))

        fn_arguments = merge_args_and_kwargs(fn_abi, args, kwargs)

        return fn_abi, fn_selector, fn_arguments

## Functions

`def encode_abi(tron, abi, arguments, data=None) ‑> Union[HexStr, str]`
  Expand source code

      def encode_abi(tron, abi, arguments, data=None) -> Union[HexStr, str]:
          argument_types = get_abi_input_types(abi)

          if not check_if_arguments_can_be_encoded(abi, arguments, {}):
              raise TypeError(
                  "One or more arguments could not be encoded to the necessary "
                  "ABI type.  Expected types are: {0}".format(
                      ', '.join(argument_types),
                  )
              )

          try:
              normalizers = [
                  abi_address_to_hex,
                  abi_bytes_to_bytes,
                  abi_string_to_text,
              ]

              normalized_arguments = map_abi_data(
                  normalizers,
                  argument_types,
                  arguments,
              )

              encoded_arguments = eth_abi_encode_abi(
                  argument_types,
                  normalized_arguments,
              )
          except EncodingError as e:
              raise TypeError(
                  "One or more arguments could not be encoded to the necessary "
                  "ABI type: {0}".format(str(e))
              )

          if data:
              return to_hex(HexBytes(data) + encoded_arguments)
          else:
              return encode_hex(encoded_arguments)

`def find_match_tron_address(test_str: str) ‑> str`
  Expand source code

      def find_match_tron_address(test_str: str) -> str:
          regex = r"\T.[a-zA-Z0-9]{32}"

          matches = re.search(regex, test_str)

          if matches:
              print("Match was found at {start}-{end}: {match}".format(start=matches.start(), end=matches.end(),
                                                                       match=matches.group()))

              for groupNum in range(0, len(matches.groups())):
                  groupNum = groupNum + 1

                  print("Group {groupNum} found at {start}-{end}: {group}".format(groupNum=groupNum,
                                                                                  start=matches.start(groupNum),
                                                                                  end=matches.end(groupNum),
                                                                                  group=matches.group(groupNum)))

          return matches

`def find_matching_fn_abi(abi, fn_identifier=None, args=None, kwargs=None)`
  Expand source code

      def find_matching_fn_abi(abi, fn_identifier=None, args=None, kwargs=None):
          args = args or tuple()
          kwargs = kwargs or dict()
          filters = []
          num_arguments = len(args) + len(kwargs)
          diagnosis = None

          if fn_identifier is FallbackFn:
              return get_fallback_func_abi(abi)

          if not is_text(fn_identifier):
              raise TypeError("Unsupported function identifier")

          name_filter = functools.partial(filter_by_name, fn_identifier)
          arg_count_filter = functools.partial(filter_by_argument_count, num_arguments)
          encoding_filter = functools.partial(filter_by_encodability, args, kwargs)
          filters.extend([
              name_filter,
              arg_count_filter,
              encoding_filter,
          ])
          function_candidates = pipe(abi, *filters)

          if len(function_candidates) == 1:
              return function_candidates[0]
          else:
              matching_identifiers = name_filter(abi)
              matching_function_signatures = [abi_to_signature(func) for func in matching_identifiers]

              arg_count_matches = len(arg_count_filter(matching_identifiers))
              encoding_matches = len(encoding_filter(matching_identifiers))

              if arg_count_matches == 0:
                  diagnosis = "\nFunction invocation failed due to improper number of arguments."
              elif encoding_matches == 0:
                  diagnosis = "\nFunction invocation failed due to no matching argument types."
              elif encoding_matches > 1:
                  diagnosis = (
                      "\nAmbiguous argument encoding. "
                      "Provided arguments can be encoded to multiple functions matching this call."
                  )
              message = (
                  "\nCould not identify the intended function with name `{name}`, "
                  "positional argument(s) of type `{arg_types}` and "
                  "keyword argument(s) of type `{kwarg_types}`."
                  "\nFound {num_candidates} function(s) with the name `{name}`: {candidates}"
                  "{diagnosis}"
              ).format(
                  name=fn_identifier,
                  arg_types=tuple(map(type, args)),
                  kwarg_types=valmap(type, kwargs),
                  num_candidates=len(matching_identifiers),
                  candidates=matching_function_signatures,
                  diagnosis=diagnosis,
              )
              raise ValueError(message)

`def get_function_info(fn_name, contract_abi=None, fn_abi=None, args=None, kwargs=None)`
  Expand source code

      def get_function_info(fn_name, contract_abi=None, fn_abi=None, args=None, kwargs=None):
          if args is None:
              args = tuple()
          if kwargs is None:
              kwargs = {}

          if fn_abi is None:
              fn_abi = find_matching_fn_abi(contract_abi, fn_name, args, kwargs)

          fn_selector = encode_hex(function_abi_to_4byte_selector(fn_abi))

          fn_arguments = merge_args_and_kwargs(fn_abi, args, kwargs)

          return fn_abi, fn_selector, fn_arguments

## Classes

`class FallbackFn`
  Expand source code

      class FallbackFn:
          pass