[
  {
    "path": ".bumpversion.cfg",
    "content": "[bumpversion]\ncurrent_version = 3.1.5\ncommit = True\ntag = True\nparse = (?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)(-(?P<stage>[^.]*)\\.(?P<devnum>\\d+))?\nserialize =\n\t{major}.{minor}.{patch}-{stage}.{devnum}\n\t{major}.{minor}.{patch}\n\n[bumpversion:part:stage]\noptional_value = stable\nfirst_value = stable\nvalues =\n\talpha\n\tbeta\n\tstable\n\n[bumpversion:part:devnum]\n\n[bumpversion:file:setup.py]\nsearch = version='{current_version}',\nreplace = version='{new_version}',\n"
  },
  {
    "path": ".coveragerc",
    "content": "[run]\nbranch = True\ninclude = */tronapi/*"
  },
  {
    "path": ".gitignore",
    "content": ".idea\n\n# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\nwheels/\n*.egg-info/\n.installed.cfg\n*.egg\nMANIFEST\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*.cover\n.hypothesis/\n.pytest_cache/\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\n*.log\nlocal_settings.py\ndb.sqlite3\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\n\n# PyBuilder\ntarget/\n\n# Jupyter Notebook\n.ipynb_checkpoints\n\n# pyenv\n.python-version\n\n# celery beat schedule file\ncelerybeat-schedule\n\n# SageMath parsed files\n*.sage.py\n\n# Environments\n.env\n.venv\nenv/\nvenv/\nENV/\nenv.bak/\nvenv.bak/\n\n# Spyder project settings\n.spyderproject\n.spyproject\n\n# Rope project settings\n.ropeproject\n\n# mkdocs documentation\n/site\n\n# mypy\n.mypy_cache/\n"
  },
  {
    "path": ".pyup.yml",
    "content": "# autogenerated pyup.io config file\n# see https://pyup.io/docs/configuration/ for all available options\n\nupdate: all\n\n# update schedule\n# default: empty\n# allowed: \"every day\", \"every week\", ..\nschedule: \"every week\"\n\npin: False"
  },
  {
    "path": ".travis.yml",
    "content": "sudo: required\nlanguage: python\nos: linux\ncache: pip\ndist: trusty\npython:\n  - \"3.6\"\n  - \"3.7-dev\"\n  - \"nightly\" # currently points to 3.7-dev\n# command to install dependencies\ninstall: \"pip install -U setuptools setuptools_scm codecov pandas\"\n# command to run tests\nscript:\n  - python setup.py test\n  - coverage run setup.py test\n  \nafter_success:\n  - codecov\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2018 iEXBase\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.rst",
    "content": "===================\nTRON API for Python\n===================\n\nA Python API for interacting with the Tron (TRX)\n\n.. image:: https://img.shields.io/pypi/v/tronapi.svg\n    :target: https://pypi.python.org/pypi/tronapi\n\n.. image:: https://img.shields.io/pypi/pyversions/tronapi.svg\n    :target: https://pypi.python.org/pypi/tronapi\n\n.. image:: https://api.travis-ci.com/iexbase/tron-api-python.svg?branch=master\n    :target: https://travis-ci.com/iexbase/tron-api-python\n    \n.. image:: https://img.shields.io/github/issues/iexbase/tron-api-python.svg\n    :target: https://github.com/iexbase/tron-api-python/issues\n    \n.. image:: https://img.shields.io/github/issues-pr/iexbase/tron-api-python.svg\n    :target: https://github.com/iexbase/tron-api-python/pulls\n\n.. image:: https://api.codacy.com/project/badge/Grade/8a5ae1e1cc834869b1094ea3b0d24f78\n   :alt: Codacy Badge\n   :target: https://app.codacy.com/app/serderovsh/tron-api-python?utm_source=github.com&utm_medium=referral&utm_content=iexbase/tron-api-python&utm_campaign=Badge_Grade_Dashboard\n    \n\n------------\n\n**A Command-Line Interface framework**\n\nYou can install it in a system-wide location via pip:\n\n.. code-block:: bash\n\n    sudo pip3 install tronapi\n\nOr install it locally using `virtualenv <https://github.com/pypa/virtualenv>`__:\n\n.. code-block:: bash\n\n    virtualenv -p /usr/bin/python3 ~/tronapi\n    source ~/tronapi/bin/activate\n    pip3 install tronapi\n\n------------\n\nUsage\n=====\nSpecify the API endpoints:\n\n\nSmart Contract\n--------------\n\n.. code-block:: python\n\n    from tronapi import Tron\n    from solc import compile_source\n\n    full_node = 'https://api.trongrid.io'\n    solidity_node = 'https://api.trongrid.io'\n    event_server = 'https://api.trongrid.io'\n\n    tron = Tron(full_node=full_node,\n            solidity_node=solidity_node,\n            event_server=event_server)\n\n    # or default (tron = Tron())\n\n\n    # Solidity source code\n    contract_source_code = '''\n    pragma solidity ^0.4.25;\n\n    contract Hello {\n        string public message;\n\n        function Hello(string initialMessage) public {\n            message = initialMessage;\n        }\n\n        function setMessage(string newMessage) public {\n            message = newMessage;\n        }\n    }\n\n    '''\n\n    compiled_sol = compile_source(contract_source_code)\n    contract_interface = compiled_sol['<stdin>:Hello']\n\n    hello = tron.trx.contract(\n        abi=contract_interface['abi'],\n        bytecode=contract_interface['bin']\n    )\n\n    # Submit the transaction that deploys the contract\n    tx = hello.deploy(\n        fee_limit=10**9,\n        call_value=0,\n        consume_user_resource_percent=1\n    )\n\n..\n\nBase Example\n------------\n\n.. code-block:: python\n    \n    from tronapi import Tron\n    logging.basicConfig(level=logging.DEBUG, format=\"%(asctime)s - %(levelname)s - %(message)s\")\n    logger = logging.getLogger()\n\n    full_node = 'https://api.trongrid.io'\n    solidity_node = 'https://api.trongrid.io'\n    event_server = 'https://api.trongrid.io'\n\n    tron = Tron(full_node=full_node,\n            solidity_node=solidity_node,\n            event_server=event_server)\n\n    account = tron.create_account\n    is_valid = bool(tron.trx.is_address(account.address.hex))\n\n    logger.debug('Generated account: ')\n    logger.debug('- Private Key: ' + account.private_key)\n    logger.debug('- Public Key: ' + account.public_key)\n    logger.debug('- Address: ')\n    logger.debug('-- Base58: ' + account.address.base58)\n    logger.debug('-- Hex: ' + account.address.hex)\n    logger.debug('-- isValid: ' + str(is_valid))\n    logger.debug('-----------')\n    \n    transaction = tron.trx.get_transaction('757a14cef293c69b1cf9b9d3d19c2e40a330c640b05c6ffa4d54609a9628758c')\n\n    logger.debug('Transaction: ')\n    logger.debug('- Hash: ' + transaction['txID'])\n    logger.debug('- Transaction: ' + json.dumps(transaction, indent=2))\n    logger.debug('-----------')\n    \n    # Events\n    event_result = tron.trx.get_event_result('TGEJj8eus46QMHPgWQe1FJ2ymBXRm96fn1', 0, 'Notify')\n\n    logger.debug('Event result:')\n    logger.debug('Contract Address: TGEJj8eus46QMHPgWQe1FJ2ymBXRm96fn1')\n    logger.debug('Event Name: Notify')\n    logger.debug('Block Number: 32162')\n    logger.debug('- Events: ' + json.dumps(event_result, indent=2))\n\nMore samples and snippets are available at `examples <https://github.com/iexbase/tron-api-python/tree/master/examples>`__.\n\nDocumentation\n=============\n\nDocumentation is available at `docs <https://tronapi-for-python.readthedocs.io/en/latest/>`__.\n\n\nDonations\n=============\n\nTRON: TRWBqiqoFZysoAeyR1J35ibuyc8EvhUAoY\n\n"
  },
  {
    "path": "codecov.yml",
    "content": "# --------------------------------------------------------------------------------------------\n# Copyright (c) iEXBase. All rights reserved.\n# Licensed under the MIT License. See License.txt in the project root for license information.\n# --------------------------------------------------------------------------------------------\n\ncodecov:\n  branch: master     # the branch to show by default\n\ncoverage:\n  precision: 2\n  round: down\n  range: \"70...100\"\n\n  status:\n    project:\n      default:\n        target: auto\n        if_no_uploads: error\n\n    patch:\n      default:\n        target: \"80%\"\n        if_no_uploads: error\n\n  ignore:          # files and folders that will be removed during processing\n    - \"docs/*\"\n    - \"examples/*\"\n\ncomment:\n  # @stevepeak (from codecov.io) suggested we change 'suggestions' to 'uncovered'\n  # in the following line. Thanks Steve!\n  layout: \"header, diff, changes, sunburst, uncovered\"\n  behavior: default\n"
  },
  {
    "path": "docs/index.rst",
    "content": "TronAPI\n=======\n\nTronAPI is a python library for interacting with Tron Protocol.\n\nContents\n--------\n\n.. toctree::\n    :maxdepth: 1\n\n    quickstart\n    overview\n    node\n    v2_migration\n    releases"
  },
  {
    "path": "docs/overview.rst",
    "content": "Overview\n========\n\n.. contents:: :local:\n\nThe common entrypoint for interacting with the Tron library is the ``Tron``\nobject.  The tron object provides APIs for interacting with the tron\nblockchain, typically by connecting to a HTTP server.\n\nProviders\n---------\n\n*Providers* are how tron connects to the blockchain.  The TronAPI library comes\nwith a the following built-in providers that should be suitable for most normal\nuse cases.\n\n- ``HttpProvider`` for connecting to http and https based servers.\n\nThe ``HttpProvider`` takes the full URI where the server can be found.  For\nlocal development this would be something like ``http://localhost:8090``.\n\n\n.. code-block:: python\n\n    >>> from tronapi import HttpProvider, Tron\n\n    # Note that you should create only one HttpProvider per\n    # process, as it recycles underlying TCP/IP network connections between\n    # your process and Tron node\n\n    >>> full_node = HttpProvider('http://localhost:8090')\n    >>> solidity_node = HttpProvider('http://localhost:8090')\n    >>> event_server = HttpProvider('http://localhost:8090')\n\n    >>> tron = Tron(full_node, solidity_node, event_server)\n\n\nBase API\n--------\n\nThe ``Tron`` class exposes the following convenience APIs.\n\n.. _overview_type_conversions:\n\nType Conversions\n~~~~~~~~~~~~~~~~\n\n.. py:method:: Tron.toHex(primitive=None, hexstr=None, text=None)\n\n    Takes a variety of inputs and returns it in its hexadecimal representation.\n\n    .. code-block:: python\n\n        >>> Tron.toHex(0)\n        '0x0'\n        >>> Tron.toHex(1)\n        '0x1'\n        >>> Tron.toHex(0x0)\n        '0x0'\n        >>> Tron.toHex(0x000F)\n        '0xf'\n        >>> Tron.toHex(b'')\n        '0x'\n        >>> Tron.toHex(b'\\x00\\x0F')\n        '0x000f'\n        >>> Tron.toHex(False)\n        '0x0'\n        >>> Tron.toHex(True)\n        '0x1'\n        >>> Tron.toHex(hexstr='0x000F')\n        '0x000f'\n        >>> Tron.toHex(hexstr='000F')\n        '0x000f'\n        >>> Tron.toHex(text='')\n        '0x'\n        >>> Tron.toHex(text='cowmö')\n        '0x636f776dc3b6'\n\n.. py:method:: Tron.toText(primitive=None, hexstr=None, text=None)\n\n    Takes a variety of inputs and returns its string equivalent.\n    Text gets decoded as UTF-8.\n\n\n    .. code-block:: python\n\n        >>> Tron.toText(0x636f776dc3b6)\n        'cowmö'\n        >>> Tron.toText(b'cowm\\xc3\\xb6')\n        'cowmö'\n        >>> Tron.toText(hexstr='0x636f776dc3b6')\n        'cowmö'\n        >>> Tron.toText(hexstr='636f776dc3b6')\n        'cowmö'\n        >>> Tron.toText(text='cowmö')\n        'cowmö'\n\n\n.. py:method:: Tron.toBytes(primitive=None, hexstr=None, text=None)\n\n    Takes a variety of inputs and returns its bytes equivalent.\n    Text gets encoded as UTF-8.\n\n\n    .. code-block:: python\n\n        >>> Tron.toBytes(0)\n        b'\\x00'\n        >>> Tron.toBytes(0x000F)\n        b'\\x0f'\n        >>> Tron.toBytes(b'')\n        b''\n        >>> Tron.toBytes(b'\\x00\\x0F')\n        b'\\x00\\x0f'\n        >>> Tron.toBytes(False)\n        b'\\x00'\n        >>> Tron.toBytes(True)\n        b'\\x01'\n        >>> Tron.toBytes(hexstr='0x000F')\n        b'\\x00\\x0f'\n        >>> Tron.toBytes(hexstr='000F')\n        b'\\x00\\x0f'\n        >>> Tron.toBytes(text='')\n        b''\n        >>> Tron.toBytes(text='cowmö')\n        b'cowm\\xc3\\xb6'\n\n\n.. py:method:: Tron.toInt(primitive=None, hexstr=None, text=None)\n\n    Takes a variety of inputs and returns its integer equivalent.\n\n\n    .. code-block:: python\n\n        >>> Tron.toInt(0)\n        0\n        >>> Tron.toInt(0x000F)\n        15\n        >>> Tron.toInt(b'\\x00\\x0F')\n        15\n        >>> Tron.toInt(False)\n        0\n        >>> Tron.toInt(True)\n        1\n        >>> Tron.toInt(hexstr='0x000F')\n        15\n        >>> Tron.toInt(hexstr='000F')\n        15\n\n.. _overview_currency_conversions:\n\nCurrency Conversions\n~~~~~~~~~~~~~~~~~~~~~\n\n.. py:method:: Tron.toSun(value)\n\n    Returns the value in the denomination specified by the ``currency`` argument\n    converted to sun.\n\n\n    .. code-block:: python\n\n        >>> tron.toSun(1)\n        1000000\n\n\n.. py:method:: Tron.fromSun(value)\n\n    Returns the value in wei converted to the given currency. The value is returned\n    as a ``Decimal`` to ensure precision down to the wei.\n\n\n    .. code-block:: python\n\n        >>> tron.fromSun(1000000)\n        Decimal('1')\n\n\n.. _overview_addresses:\n\nAddresses\n~~~~~~~~~~~~~~~~\n\n.. py:method:: Tron.isAddress(value)\n\n    Returns ``True`` if the value is one of the recognized address formats.\n\n    .. code-block:: python\n\n        >>> tron.isAddress('TRWBqiqoFZysoAeyR1J35ibuyc8EvhUAoY')\n        True\n\n\n.. _overview_hashing:\n\n\nCryptographic Hashing\n~~~~~~~~~~~~~~~~~~~~~\n\n.. py:classmethod:: Tron.sha3(primitive=None, hexstr=None, text=None)\n\n    Returns the Keccak SHA256 of the given value. Text is encoded to UTF-8 before\n    computing the hash, just like Solidity. Any of the following are\n    valid and equivalent:\n\n    .. code-block:: python\n\n        >>> Tron.sha3(0x747874)\n        >>> Tron.sha3(b'\\x74\\x78\\x74')\n        >>> Tron.sha3(hexstr='0x747874')\n        >>> Tron.sha3(hexstr='747874')\n        >>> Tron.sha3(text='txt')\n        HexBytes('0xd7278090a36507640ea6b7a0034b69b0d240766fa3f98e3722be93c613b29d2e')\n\n"
  },
  {
    "path": "docs/quickstart.rst",
    "content": "Quickstart\n==========\n\n.. contents:: :local:\n\n.. NOTE:: All code starting with a ``$`` is meant to run on your terminal.\n    All code starting with a ``>>>`` is meant to run in a python interpreter,\n    like `ipython <https://pypi.org/project/ipython/>`_.\n\nInstallation\n------------\n\nTronAPI can be installed (preferably in a :ref:`virtualenv <setup_environment>`)\nusing ``pip`` as follows:\n\n.. code-block:: shell\n\n   $ pip install tronapi\n\n\n.. NOTE:: If you run into problems during installation, you might have a\n    broken environment. See the troubleshooting guide to :ref:`setup_environment`.\n\n\nInstallation from source can be done from the root of the project with the\nfollowing command.\n\n.. code-block:: shell\n\n   $ pip install .\n\n\nUsing TronAPI\n----------\n\nTo use the tron library you will need to initialize the\n:class:`~tronapi` class.\n\n.. code-block:: python\n\n    >>> from tronapi import Tron\n    >>> full_node = HttpProvider('https://api.trongrid.io')\n    >>> solidity_node = HttpProvider('https://api.trongrid.io')\n    >>> event_server = 'https://api.trongrid.io'\n    >>>\n    >>> tron = Tron(full_node, solidity_node, event_server)\n    >>> tron.default_block = 'latest'\n\n\nGetting Blockchain Info\n----------------------------------------\n\nIt's time to start using TronAPI for Python! Try getting all the information about the latest block.\n\n.. code-block:: python\n\n    >>> tron.get_block('latest')\n    >>> {\n    \"blockID\": \"00000000003a5bbda4aea15cb5d99230674463e9d5f2c0c647316839b25fd5b9\",\n    \"block_header\": {\n        \"raw_data\": {\n            \"number\": 3824573,\n            \"txTrieRoot\": \"31ee3e2ed28f843bf1d53495beece2f5b9c76480772f0106e17156fb0066c3a2\",\n            \"witness_address\": \"41f70386347e689e6308e4172ed7319c49c0f66e0b\",\n            \"parentHash\": \"00000000003a5bbc1e78e3144ad52f01a27b8f7acceb98d3ca09c1abea5cd32a\",\n            \"version\": 3,\n            \"timestamp\": 1541425827000\n        },\n        \"witness_signature\": \"fddc729f55c0ecc6f9cf4ab17cf818ddc0e85d2c21382ed6b1430adb1dcd13006c24ae0e08f16d29362452ec8869d29a28d57a85d6cec30ef60c2a37332fdb4d00\"\n    },\n    \"transactions\": [\n\n    ]\n}\n\n\n\n"
  },
  {
    "path": "docs/releases.rst",
    "content": ""
  },
  {
    "path": "docs/v2_migration.rst",
    "content": "Migrating your code from v1 to v2\n=======================================\n\nChanges to base API convenience methods\n---------------------------------------\n\nTron.toDecimal()\n~~~~~~~~~~~~~~~~~\n\nIn v4 ``Tron.toDecimal()`` is renamed: :meth:`~Tron.toInt` for improved clarity. It does not return a :class:`decimal.Decimal`, it returns an :class:`int`.\n\n\nRemoved Methods\n~~~~~~~~~~~~~~~~~~\n\n- ``Tron.toUtf8`` was removed for :meth:`~Tron.toText`.\n- ``Tron.fromUtf8`` was removed for :meth:`~Tron.toHex`.\n- ``Tron.toAscii`` was removed for :meth:`~Tron.toBytes`.\n- ``Tron.fromAscii`` was removed for :meth:`~Tron.toHex`.\n- ``Tron.fromDecimal`` was removed for :meth:`~Tron.toHex`.\n\nProvider Access\n~~~~~~~~~~~~~~~~~\n\nIn v2, ``tron.currentProvider`` was removed, in favor of ``tron.providers``.\n\nDisambiguating String Inputs\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThere are a number of places where an arbitrary string input might be either\na byte-string that has been hex-encoded, or unicode characters in text.\nThese are named ``hexstr`` and ``text`` in TronAPI.\nYou specify which kind of :class:`str` you have by using the appropriate\nkeyword argument. See examples in :ref:`overview_type_conversions`.\n\nIn v1, some methods accepted a :class:`str` as the first positional argument.\nIn v2, you must pass strings as one of ``hexstr`` or ``text`` keyword arguments.\n\nNotable methods that no longer accept ambiguous strings:\n\n- :meth:`~Tron.sha3`\n- :meth:`~Tron.toBytes`"
  },
  {
    "path": "example.py",
    "content": "import json\nimport logging\n\nfrom tronapi import Tron\n\nlogging.basicConfig(level=logging.DEBUG, format=\"%(asctime)s - %(levelname)s - %(message)s\")\nlogger = logging.getLogger()\n\nfull_node = 'https://api.trongrid.io'\nsolidity_node = 'https://api.trongrid.io'\nevent_server = 'https://api.trongrid.io/'\nprivate_key = 'da146374a75310b9666e834ee4ad0866d6f4035967bfc76217c5a495fff9f0d0'\n\ntron = Tron(full_node=full_node,\n            solidity_node=solidity_node,\n            event_server=event_server)\n\n\naccount = tron.create_account\nis_valid = bool(tron.isAddress(account.address.hex))\n\n\nlogger.debug('Generated account: ')\nlogger.debug('- Private Key: ' + account.private_key)\nlogger.debug('- Public Key: ' + account.public_key)\nlogger.debug('- Address: ')\nlogger.debug('-- Base58: ' + account.address.base58)\nlogger.debug('-- Hex: ' + account.address.hex)\nlogger.debug('-- isValid: ' + str(is_valid))\nlogger.debug('-----------')\n\ncurrent_block = tron.trx.get_current_block()\nlogger.debug('Current block: ')\nlogger.debug(json.dumps(current_block, indent=2))\nlogger.debug('-----------')\n\nprevious_block = tron.trx.get_block(0)\n\nlogger.debug('Previous block #52: ')\nlogger.debug(json.dumps(previous_block, indent=2))\nlogger.debug('-----------')\n\n\ngenesis_block_count = tron.trx.get_block_transaction_count('earliest')\nlogger.debug('Genesis Block Transaction Count: ')\nlogger.debug('Transactions:' + str(genesis_block_count))\nlogger.debug('-----------')\n\ntransaction = tron.trx.get_transaction('757a14cef293c69b1cf9b9d3d19c2e40a330c640b05c6ffa4d54609a9628758c')\n\nlogger.debug('Transaction: ')\nlogger.debug('- Hash: ' + transaction['txID'])\nlogger.debug('- Transaction: ' + json.dumps(transaction, indent=2))\nlogger.debug('-----------')\n\n\naccount_info = tron.trx.get_account('TKLnCNY5EsLNCvCXQTCn1dtqvc6vHhJUyJ')\n\nlogger.debug('Account information: ')\nlogger.debug('- Address: TKLnCNY5EsLNCvCXQTCn1dtqvc6vHhJUyJ')\nlogger.debug('- Account:' + json.dumps(account_info, indent=2))\nlogger.debug('-----------')\n\n\nbalance = tron.trx.get_account('TKLnCNY5EsLNCvCXQTCn1dtqvc6vHhJUyJ')\n\nlogger.debug('Account balance: ')\nlogger.debug('- Address: TKLnCNY5EsLNCvCXQTCn1dtqvc6vHhJUyJ')\nlogger.debug('- Account:' + json.dumps(balance, indent=2))\nlogger.debug('-----------')\n\n\nband_width = tron.trx.get_band_width('TKLnCNY5EsLNCvCXQTCn1dtqvc6vHhJUyJ')\n\nlogger.debug('Account bandwidth: ')\nlogger.debug('- Address: TKLnCNY5EsLNCvCXQTCn1dtqvc6vHhJUyJ')\nlogger.debug('- Bandwidth:' + json.dumps(band_width, indent=2))\nlogger.debug('-----------')\n\n\nlist_nodes = tron.trx.list_nodes()\n\nlogger.debug('List of full nodes: ')\nlogger.debug('- Node Count:' + str(len(list_nodes)))\nlogger.debug('- Nodes:' + json.dumps(list_nodes, indent=2))\nlogger.debug('-----------')\n\n\nblock_ids = tron.trx.get_block_range(30, 35)\nblock = list(map(lambda x: {'id': x['block_header']['raw_data']['number'] or 0}, block_ids))\n\nlogger.debug('Block IDs between 30 and 35: ')\nlogger.debug('- Block Range: [ 30, 35 ]')\nlogger.debug('- Blocks IDs:' + json.dumps(block, indent=2))\nlogger.debug('-----------')\n\n\n# send = tron.send_trx('TGEJj8eus46QMHPgWQe1FJ2ymBXRm96fn1', 10)\n# logger.debug('Send TRX transaction: ')\n# logger.debug('- Result: ' + json.dumps(send, indent=2))\n# logger.debug('-----------')\n\n\nevent_result = tron.get_event_result('TGEJj8eus46QMHPgWQe1FJ2ymBXRm96fn1', 0, 'Notify')\n\nlogger.debug('Event result:')\nlogger.debug('Contract Address: TGEJj8eus46QMHPgWQe1FJ2ymBXRm96fn1')\nlogger.debug('Event Name: Notify')\nlogger.debug('Block Number: 32162')\nlogger.debug('- Events: ' + json.dumps(event_result, indent=2))\n\n\nevent_by_transaction_id = tron.get_event_transaction_id('32d7efe5f70c044bcd831f21f911209a7abf4ed0d5934b2c1b804e108008cd43')\n\nlogger.debug('Specific event result:')\nlogger.debug('Transaction: 32d7efe5f70c044bcd831f21f911209a7abf4ed0d5934b2c1b804e108008cd43')\nlogger.debug('- Events: ' + json.dumps(event_by_transaction_id, indent=2))\n\n\nfirst_transaction = tron.trx.get_transaction_from_block(0, 0)\n\nlogger.debug('First transaction from block 0')\nlogger.debug('- Transaction: ' + json.dumps(first_transaction, indent=2))\n\n"
  },
  {
    "path": "examples/account.py",
    "content": "\nimport logging\nfrom tronapi import Tron\n\nlogging.basicConfig(level=logging.DEBUG, format=\"%(asctime)s - %(levelname)s - %(message)s\")\nlogger = logging.getLogger()\n\nfull_node = 'https://api.trongrid.io'\nsolidity_node = 'https://api.trongrid.io'\nevent_server = 'https://api.trongrid.io/'\nprivate_key = 'da146374a75310b9666e834ee4ad0866d6f4035967bfc76217c5a495fff9f0d0'\n\ntron = Tron(full_node=full_node,\n            solidity_node=solidity_node,\n            event_server=event_server)\n\n\naccount = tron.create_account\nis_valid = bool(tron.isAddress(account.address.hex))\n\n\nlogger.debug('Generated account: ')\nlogger.debug('- Private Key: ' + account.private_key)\nlogger.debug('- Public Key: ' + account.public_key)\nlogger.debug('- Address: ')\nlogger.debug('-- Base58: ' + account.address.base58)\nlogger.debug('-- Hex: ' + account.address.hex)\nlogger.debug('-- isValid: ' + str(is_valid))\nlogger.debug('-----------')\n\n"
  },
  {
    "path": "examples/address-hex.py",
    "content": "from tronapi import Tron\nfrom tronapi import HttpProvider\n\nfull_node = HttpProvider('https://api.trongrid.io')\nsolidity_node = HttpProvider('https://api.trongrid.io')\nevent_server = HttpProvider('https://api.trongrid.io')\ntron = Tron(full_node=full_node,\n            solidity_node=solidity_node,\n            event_server=event_server)\n\n\ntron.address.to_hex('TT67rPNwgmpeimvHUMVzFfKsjL9GZ1wGw8')\n# result: 41BBC8C05F1B09839E72DB044A6AA57E2A5D414A10\n\ntron.address.from_hex('41BBC8C05F1B09839E72DB044A6AA57E2A5D414A10')\n# result: TT67rPNwgmpeimvHUMVzFfKsjL9GZ1wGw8\n"
  },
  {
    "path": "examples/amount.py",
    "content": "from tronapi import Tron\nfrom tronapi import HttpProvider\n\nfull_node = HttpProvider('https://api.trongrid.io')\nsolidity_node = HttpProvider('https://api.trongrid.io')\nevent_server = HttpProvider('https://api.trongrid.io')\ntron = Tron(full_node=full_node,\n            solidity_node=solidity_node,\n            event_server=event_server)\n\n\n\ntron.toSun(1)\n# result: 1000000\n\ntron.fromSun(1000000)\n# result: 1\n\n\n"
  },
  {
    "path": "examples/contract.py",
    "content": "from tronapi import Tron, HttpProvider\nfrom solc import compile_source\n\nfull_node = HttpProvider('https://api.trongrid.io')\nsolidity_node = HttpProvider('https://api.trongrid.io')\nevent_server = HttpProvider('https://api.trongrid.io')\n\ntron = Tron(full_node=full_node,\n            solidity_node=solidity_node,\n            event_server=event_server)\n\n\n# Solidity source code\ncontract_source_code = '''\npragma solidity ^0.4.25;\n\ncontract Hello {\n    string public message;\n\n    function Hello(string initialMessage) public {\n        message = initialMessage;\n    }\n\n    function setMessage(string newMessage) public {\n        message = newMessage;\n    }\n}\n\n'''\n\ncompiled_sol = compile_source(contract_source_code)\ncontract_interface = compiled_sol['<stdin>:Hello']\n\nhello = tron.trx.contract(\n    abi=contract_interface['abi'],\n    bytecode=contract_interface['bin']\n)\n\n# Submit the transaction that deploys the contract\ntx_data = hello.deploy(\n    fee_limit=10**9,\n    call_value=0,\n    consume_user_resource_percent=1\n)\n\nsign = tron.trx.sign(tx_data)\nresult = tron.trx.broadcast(sign)\n"
  },
  {
    "path": "examples/custom-nodes.py",
    "content": "from tronapi import Tron\nfrom tronapi import HttpProvider\n\nfull_node = HttpProvider('https://api.trongrid.io')\nsolidity_node = HttpProvider('https://api.trongrid.io')\nevent_server = HttpProvider('https://api.trongrid.io')\n\n# option 1\ntron = Tron(full_node=full_node,\n            solidity_node=solidity_node,\n            event_server=event_server)\n\n# option 2\ntron_v2 = Tron()\n\n# option 3\ntron_v3 = Tron(\n    default_address='TRWBqiqoFZysoAeyR1J35ibuyc8EvhUAoY',\n    private_key='...'\n)\n"
  },
  {
    "path": "examples/find-transaction.py",
    "content": "from tronapi import Tron\nfrom tronapi import HttpProvider\n\nfull_node = HttpProvider('https://api.trongrid.io')\nsolidity_node = HttpProvider('https://api.trongrid.io')\nevent_server = HttpProvider('https://api.trongrid.io')\ntron = Tron(full_node=full_node,\n            solidity_node=solidity_node,\n            event_server=event_server)\n\n\n\nresult = tron.trx.get_transaction('TxId')\n"
  },
  {
    "path": "examples/send-transaction.py",
    "content": "from tronapi import Tron\nfrom tronapi import HttpProvider\n\nfull_node = HttpProvider('https://api.trongrid.io')\nsolidity_node = HttpProvider('https://api.trongrid.io')\nevent_server = HttpProvider('https://api.trongrid.io')\n\ntron = Tron(full_node=full_node,\n            solidity_node=solidity_node,\n            event_server=event_server)\n\n\ntron.private_key = 'private_key'\ntron.default_address = 'default address'\n\n# added message\nsend = tron.trx.send_transaction('to', 1)\n\nprint(send)\n"
  },
  {
    "path": "examples/sign.py",
    "content": "from tronapi import Tron\nfrom tronapi import HttpProvider\n\nfull_node = HttpProvider('https://api.trongrid.io')\nsolidity_node = HttpProvider('https://api.trongrid.io')\nevent_server = HttpProvider('https://api.trongrid.io')\n\ntron = Tron(full_node=full_node,\n            solidity_node=solidity_node,\n            event_server=event_server)\n\n\ntron.private_key = 'private_key'\ntron.default_address = 'default address'\n\n# create transaction\ncreate_tx = tron.transaction_builder.send_transaction('to', 1, 'from')\n\n# offline sign\noffline_sign = tron.trx.sign(create_tx)\n\n\n# online sign (Not recommended)\nonline_sign = tron.trx.online_sign(create_tx)\n\n"
  },
  {
    "path": "requirements-docs.txt",
    "content": ".[docs]\n"
  },
  {
    "path": "setup.py",
    "content": "#!/usr/bin/env python\n# --------------------------------------------------------------------\n# Copyright (c) iEXBase. All rights reserved.\n# Licensed under the MIT License.\n# See License.txt in the project root for license information.\n# --------------------------------------------------------------------\n\n\"\"\"\n    setup\n    =====\n\n    Tron: A Python API for interacting with Tron (TRX)\n\n    :copyright: © 2018 by the iEXBase.\n    :license: MIT License\n\"\"\"\n\nimport os\nimport platform\nfrom setuptools import (\n    find_packages,\n    setup,\n)\n\npy_version = platform.python_version()\n\nPACKAGE_VERSION = '3.1.5'\n\nEXTRAS_REQUIRE = {\n    'tester': [\n        'coverage',\n        'pep8',\n        'pyflakes',\n        'pylint',\n        'pytest-cov'\n    ],\n\n    'docs': [\n        \"mock\",\n        \"sphinx-better-theme>=0.1.4\",\n        \"click>=5.1\",\n        \"configparser==3.5.0\",\n        \"contextlib2>=0.5.4\",\n        \"py-solc>=0.4.0\",\n        \"pytest>=2.7.2\",\n        \"sphinx\",\n        \"sphinx_rtd_theme>=0.1.9\",\n        \"toposort>=1.4\",\n        \"urllib3\",\n        \"tronapi\",\n        \"wheel >= 0.31.0\"\n    ],\n\n    'dev': [\n        \"bumpversion\",\n        \"flaky>=3.3.0\",\n        \"hypothesis>=3.31.2\",\n        \"pytest>=3.5.0,<4\",\n        \"pytest-mock==1.*\",\n        \"pytest-pythonpath>=0.3\",\n        \"pytest-watch==4.*\",\n        \"pytest-xdist==1.*\",\n        \"setuptools>=38.6.0\",\n        \"tox>=1.8.0\",\n        \"twine >= 1.11.0\",\n        \"tqdm\",\n        \"when-changed\"\n    ]\n\n}\n\nEXTRAS_REQUIRE['dev'] = (\n        EXTRAS_REQUIRE['tester'] +\n        EXTRAS_REQUIRE['docs'] +\n        EXTRAS_REQUIRE['dev']\n)\n\ninstall_requires = [\n    \"toolz>=0.9.0,<1.0.0;implementation_name=='pypy'\",\n    \"cytoolz>=0.9.0,<1.0.0;implementation_name=='cpython'\",\n\n    \"eth-abi>=2.0.0b6,<3.0.0\",\n    \"eth-account==0.4.0\",\n    \"eth-utils>=1.3.0,<2.0.0\",\n    \"eth-hash[pycryptodome]>=0.2.0,<1.0.0\",\n\n    \"trx-utils\",\n\n    \"hexbytes>=0.1.0,<1.0.0\",\n\n    \"requests>=2.16.0,<3.0.0\",\n    \"base58\",\n    \"ecdsa\",\n    'attrdict',\n]\n\nthis_dir = os.path.dirname(__file__)\nreadme_filename = os.path.join(this_dir, 'README.rst')\n\nwith open(readme_filename) as f:\n    PACKAGE_LONG_DESCRIPTION = f.read()\n\n\nsetup(\n    name='tronapi',\n    version=PACKAGE_VERSION,\n    description='A Python API for interacting with Tron (TRX)',\n    long_description=PACKAGE_LONG_DESCRIPTION,\n    long_description_content_type='text/x-rst',\n    keywords='tron tron-api tron-api-python iexbase',\n    url='https://github.com/iexbase/tron-api-python',\n    author='Shamsudin Serderov',\n    author_email='steein.shamsudin@gmail.com',\n    license='MIT License',\n    zip_safe=False,\n    python_requires='>=3.6,<4',\n    classifiers=[\n        'Development Status :: 3 - Alpha',\n        'Intended Audience :: Developers',\n        'License :: OSI Approved :: MIT License',\n        'Natural Language :: English',\n        'Programming Language :: Python :: 3',\n        'Programming Language :: Python :: 3.6',\n        'Programming Language :: Python :: 3.7',\n    ],\n    packages=find_packages(exclude=['examples']),\n    include_package_data=True,\n    install_requires=install_requires,\n    tests_require=EXTRAS_REQUIRE['tester'],\n    extras_require=EXTRAS_REQUIRE,\n)\n"
  },
  {
    "path": "tronapi/__init__.py",
    "content": "# --------------------------------------------------------------------\n# Copyright (c) iEXBase. All rights reserved.\n# Licensed under the MIT License.\n# See License.txt in the project root for license information.\n# --------------------------------------------------------------------\n\nimport sys\n\nimport pkg_resources\n\nfrom eth_account import Account  # noqa: E402\nfrom tronapi.providers.http import HttpProvider  # noqa: E402\nfrom tronapi.main import Tron  # noqa: E402\n\nif sys.version_info < (3, 5):\n    raise EnvironmentError(\"Python 3.5 or above is required\")\n\n\n__version__ = pkg_resources.get_distribution(\"tronapi\").version\n\n__all__ = [\n    '__version__',\n    'HttpProvider',\n    'Account',\n    'Tron',\n]\n"
  },
  {
    "path": "tronapi/common/__init__.py",
    "content": ""
  },
  {
    "path": "tronapi/common/abi.py",
    "content": "# --------------------------------------------------------------------\n# Copyright (c) iEXBase. All rights reserved.\n# Licensed under the MIT License.\n# See License.txt in the project root for license information.\n# --------------------------------------------------------------------\n\nimport binascii\nimport itertools\nimport re\n\nfrom collections import (\n    namedtuple,\n)\n\nfrom eth_abi import (\n    encoding,\n    decoding\n)\n\nfrom eth_abi.codec import ABICodec\nfrom eth_abi.registry import (\n    BaseEquals,\n    registry as default_registry,\n)\n\nfrom eth_utils import to_tuple\n\nfrom trx_utils import (\n    decode_hex,\n    is_bytes,\n    is_text,\n    to_text\n)\n\nfrom tronapi.common.formatters import recursive_map\nfrom tronapi.exceptions import FallbackNotFound\nfrom tronapi.common.toolz import (\n    curry,\n    partial,\n    pipe,\n)\n\nDYNAMIC_TYPES = ['bytes', 'string']\n\nINT_SIZES = range(8, 257, 8)\nBYTES_SIZES = range(1, 33)\nUINT_TYPES = ['uint{0}'.format(i) for i in INT_SIZES]\nINT_TYPES = ['int{0}'.format(i) for i in INT_SIZES]\nBYTES_TYPES = ['bytes{0}'.format(i) for i in BYTES_SIZES] + ['bytes32.byte']\n\nSTATIC_TYPES = list(itertools.chain(\n    ['address', 'bool'],\n    UINT_TYPES,\n    INT_TYPES,\n    BYTES_TYPES,\n))\n\nBASE_TYPE_REGEX = '|'.join((\n    _type + '(?![a-z0-9])'\n    for _type\n    in itertools.chain(STATIC_TYPES, DYNAMIC_TYPES)\n))\n\nSUB_TYPE_REGEX = (\n    r'\\['\n    '[0-9]*'\n    r'\\]'\n)\n\nTYPE_REGEX = (\n    '^'\n    '(?:{base_type})'\n    '(?:(?:{sub_type})*)?'\n    '$'\n).format(\n    base_type=BASE_TYPE_REGEX,\n    sub_type=SUB_TYPE_REGEX,\n)\n\nNAME_REGEX = (\n    '[a-zA-Z_]'\n    '[a-zA-Z0-9_]*'\n)\n\nENUM_REGEX = (\n    '^'\n    '{lib_name}'\n    r'\\.'\n    '{enum_name}'\n    '$'\n).format(lib_name=NAME_REGEX, enum_name=NAME_REGEX)\n\nEND_BRACKETS_OF_ARRAY_TYPE_REGEX = r\"\\[[^]]*\\]$\"\n\nNAME_REGEX = (\n    '[a-zA-Z_]'\n    '[a-zA-Z0-9_]*'\n)\n\nARRAY_REGEX = (\n    \"^\"\n    \"[a-zA-Z0-9_]+\"\n    \"({sub_type})+\"\n    \"$\"\n).format(sub_type=SUB_TYPE_REGEX)\n\n\ndef filter_by_argument_name(argument_names, contract_abi):\n    return [\n        abi\n        for abi in contract_abi\n        if set(argument_names).intersection(\n            get_abi_input_names(abi)\n        ) == set(argument_names)\n    ]\n\n\ntry:\n    from eth_abi.abi import (\n        process_type,\n        collapse_type,\n    )\nexcept ImportError:\n    from eth_abi.grammar import (\n        parse as parse_type_string,\n        normalize as normalize_type_string,\n        TupleType,\n    )\n\n\n    def process_type(type_str):\n        normalized_type_str = normalize_type_string(type_str)\n        abi_type = parse_type_string(normalized_type_str)\n\n        if isinstance(abi_type, TupleType):\n            type_str_repr = repr(type_str)\n            if type_str != normalized_type_str:\n                type_str_repr = '{} (normalized to {})'.format(\n                    type_str_repr,\n                    repr(normalized_type_str),\n                )\n\n            raise ValueError(\n                \"Cannot process type {}: tuple types not supported\".format(\n                    type_str_repr,\n                )\n            )\n\n        abi_type.validate()\n\n        sub = abi_type.sub\n        if isinstance(sub, tuple):\n            sub = 'x'.join(map(str, sub))\n        elif isinstance(sub, int):\n            sub = str(sub)\n        else:\n            sub = ''\n\n        arrlist = abi_type.arrlist\n        if isinstance(arrlist, tuple):\n            arrlist = list(map(list, arrlist))\n        else:\n            arrlist = []\n\n        return abi_type.base, sub, arrlist\n\n\n    def collapse_type(base, sub, arrlist):\n        return base + str(sub) + ''.join(map(repr, arrlist))\n\n\ndef filter_by_type(_type, contract_abi):\n    return [abi for abi in contract_abi if abi['type'] == _type]\n\n\ndef filter_by_name(name, contract_abi):\n    return [\n        abi\n        for abi\n        in contract_abi\n        if (\n                abi['type'] not in ('fallback', 'constructor') and\n                abi['name'] == name\n        )\n    ]\n\n\ndef get_abi_input_types(abi):\n    if 'inputs' not in abi and abi['type'] == 'fallback':\n        return []\n    else:\n        return [arg['type'] for arg in abi['inputs']]\n\n\ndef get_abi_output_types(abi):\n    if abi['type'] == 'fallback':\n        return []\n    else:\n        return [arg['type'] for arg in abi['outputs']]\n\n\ndef get_abi_input_names(abi):\n    if 'inputs' not in abi and abi['type'] == 'fallback':\n        return []\n    else:\n        return [arg['name'] for arg in abi['inputs']]\n\n\ndef length_of_array_type(abi_type):\n    if not is_array_type(abi_type):\n        raise ValueError(\n            \"Cannot parse length of nonarray abi-type: {0}\".format(abi_type)\n        )\n\n    inner_brackets = re.search(END_BRACKETS_OF_ARRAY_TYPE_REGEX, abi_type).group(0).strip(\"[]\")\n    if not inner_brackets:\n        return None\n    else:\n        return int(inner_brackets)\n\n\ndef get_fallback_func_abi(contract_abi):\n    fallback_abis = filter_by_type('fallback', contract_abi)\n    if fallback_abis:\n        return fallback_abis[0]\n    else:\n        raise FallbackNotFound(\"No fallback function was found in the contract ABI.\")\n\n\ndef fallback_func_abi_exists(contract_abi):\n    return filter_by_type('fallback', contract_abi)\n\n\ndef get_constructor_abi(contract_abi):\n    candidates = [\n        abi for abi in contract_abi if abi['type'] == 'constructor'\n    ]\n    if len(candidates) == 1:\n        return candidates[0]\n    elif len(candidates) == 0:\n        return None\n    elif len(candidates) > 1:\n        raise ValueError(\"Found multiple constructors.\")\n\n\ndef is_recognized_type(abi_type):\n    return bool(re.match(TYPE_REGEX, abi_type))\n\n\ndef is_bool_type(abi_type):\n    return abi_type == 'bool'\n\n\ndef is_uint_type(abi_type):\n    return abi_type in UINT_TYPES\n\n\ndef is_int_type(abi_type):\n    return abi_type in INT_TYPES\n\n\ndef is_address_type(abi_type):\n    return abi_type == 'address'\n\n\ndef is_bytes_type(abi_type):\n    return abi_type in BYTES_TYPES + ['bytes']\n\n\ndef is_string_type(abi_type):\n    return abi_type == 'string'\n\n\n@curry\ndef is_length(target_length, value):\n    return len(value) == target_length\n\n\ndef size_of_type(abi_type):\n    \"\"\"\n    Returns size in bits of abi_type\n    \"\"\"\n    if 'string' in abi_type:\n        return None\n    if 'byte' in abi_type:\n        return None\n    if '[' in abi_type:\n        return None\n    if abi_type == 'bool':\n        return 8\n    if abi_type == 'address':\n        return 160\n    return int(re.sub(r\"\\D\", \"\", abi_type))\n\n\ndef is_array_type(abi_type):\n    return bool(re.match(ARRAY_REGEX, abi_type))\n\n\ndef sub_type_of_array_type(abi_type):\n    if not is_array_type(abi_type):\n        raise ValueError(\n            \"Cannot parse subtype of nonarray abi-type: {0}\".format(abi_type)\n        )\n\n    return re.sub(END_BRACKETS_OF_ARRAY_TYPE_REGEX, '', abi_type, 1)\n\n\ndef is_probably_enum(abi_type):\n    return bool(re.match(ENUM_REGEX, abi_type))\n\n\n@to_tuple\ndef normalize_event_input_types(abi_args):\n    for arg in abi_args:\n        if is_recognized_type(arg['type']):\n            yield arg\n        elif is_probably_enum(arg['type']):\n            yield {k: 'uint8' if k == 'type' else v for k, v in arg.items()}\n        else:\n            yield arg\n\n\ndef abi_to_signature(abi):\n    function_signature = \"{fn_name}({fn_input_types})\".format(\n        fn_name=abi['name'],\n        fn_input_types=','.join([\n            arg['type'] for arg in normalize_event_input_types(abi.get('inputs', []))\n        ]),\n    )\n    return function_signature\n\n\ndef filter_by_argument_count(num_arguments, contract_abi):\n    return [\n        abi\n        for abi\n        in contract_abi\n        if len(abi['inputs']) == num_arguments\n    ]\n\n\ndef filter_by_encodability(args, kwargs, contract_abi):\n    return [\n        function_abi\n        for function_abi\n        in contract_abi\n        if check_if_arguments_can_be_encoded(function_abi, args, kwargs)\n    ]\n\nclass AcceptsHexStrMixin:\n    def validate_value(self, value):\n        if is_text(value):\n            try:\n                value = decode_hex(value)\n            except binascii.Error:\n                self.invalidate_value(\n                    value,\n                    msg='invalid hex string',\n                )\n\n        super().validate_value(value)\n\n\nclass ByteStringEncoder(AcceptsHexStrMixin, encoding.ByteStringEncoder):\n    pass\n\n\nclass BytesEncoder(AcceptsHexStrMixin, encoding.BytesEncoder):\n    pass\n\n\nclass TextStringEncoder(encoding.TextStringEncoder):\n    @classmethod\n    def validate_value(cls, value):\n        if is_bytes(value):\n            try:\n                value = to_text(value)\n            except UnicodeDecodeError:\n                cls.invalidate_value(\n                    value,\n                    msg='not decodable as unicode string',\n                )\n\n        super().validate_value(value)\n\n\n# We make a copy here just to make sure that eth-abi's default registry is not\n# affected by our custom encoder subclasses\nregistry = default_registry.copy()\n\nregistry.unregister('address')\nregistry.unregister('bytes<M>')\nregistry.unregister('bytes')\nregistry.unregister('string')\n\nregistry.register(\n    BaseEquals('bytes', with_sub=True),\n    BytesEncoder, decoding.BytesDecoder,\n    label='bytes<M>',\n)\n\nregistry.register(\n    BaseEquals('bytes', with_sub=False),\n    ByteStringEncoder, decoding.ByteStringDecoder,\n    label='bytes',\n)\nregistry.register(\n    BaseEquals('string'),\n    TextStringEncoder, decoding.StringDecoder,\n    label='string',\n)\n\ncodec = ABICodec(registry)\nis_encodable = codec.is_encodable\n\n\ndef check_if_arguments_can_be_encoded(function_abi, args, kwargs):\n    try:\n        arguments = merge_args_and_kwargs(function_abi, args, kwargs)\n    except TypeError:\n        return False\n\n    if len(function_abi.get('inputs', [])) != len(arguments):\n        return False\n\n    types = get_abi_input_types(function_abi)\n\n    return all(\n        is_encodable(_type, arg)\n        for _type, arg in zip(types, arguments)\n    )\n\n\ndef merge_args_and_kwargs(function_abi, args, kwargs):\n    if len(args) + len(kwargs) != len(function_abi.get('inputs', [])):\n        raise TypeError(\n            \"Incorrect argument count.  Expected '{0}'.  Got '{1}'\".format(\n                len(function_abi['inputs']),\n                len(args) + len(kwargs),\n            )\n        )\n\n    if not kwargs:\n        return args\n\n    args_as_kwargs = {\n        arg_abi['name']: arg\n        for arg_abi, arg in zip(function_abi['inputs'], args)\n    }\n    duplicate_keys = set(args_as_kwargs).intersection(kwargs.keys())\n    if duplicate_keys:\n        raise TypeError(\n            \"{fn_name}() got multiple values for argument(s) '{dups}'\".format(\n                fn_name=function_abi['name'],\n                dups=', '.join(duplicate_keys),\n            )\n        )\n\n    sorted_arg_names = [arg_abi['name'] for arg_abi in function_abi['inputs']]\n\n    unknown_kwargs = {key for key in kwargs.keys() if key not in sorted_arg_names}\n    if unknown_kwargs:\n        if function_abi.get('name'):\n            raise TypeError(\n                \"{fn_name}() got unexpected keyword argument(s) '{dups}'\".format(\n                    fn_name=function_abi.get('name'),\n                    dups=', '.join(unknown_kwargs),\n                )\n            )\n        # show type instead of name in the error message incase key 'name' is missing.\n        raise TypeError(\n            \"Type: '{_type}' got unexpected keyword argument(s) '{dups}'\".format(\n                _type=function_abi.get('type'),\n                dups=', '.join(unknown_kwargs),\n            )\n        )\n\n    sorted_args = list(zip(\n        *sorted(\n            itertools.chain(kwargs.items(), args_as_kwargs.items()),\n            key=lambda kv: sorted_arg_names.index(kv[0])\n        )\n    ))\n    if sorted_args:\n        return sorted_args[1]\n    else:\n        return tuple()\n\n\ndef abi_sub_tree(data_type, data_value):\n    if data_type is None:\n        return ABITypedData([None, data_value])\n\n    try:\n        base, sub, arrlist = data_type\n    except ValueError:\n        base, sub, arrlist = process_type(data_type)\n\n    collapsed = collapse_type(base, sub, arrlist)\n\n    if arrlist:\n        sub_type = (base, sub, arrlist[:-1])\n        return ABITypedData([\n            collapsed,\n            [\n                abi_sub_tree(sub_type, sub_value)\n                for sub_value in data_value\n            ],\n        ])\n    else:\n        return ABITypedData([collapsed, data_value])\n\n\n@curry\ndef map_abi_data(normalizers, types, data):\n    \"\"\"\n        This function will apply normalizers to your data, in the\n    context of the relevant types. Each normalizer is in the format:\n\n    def normalizer(datatype, data):\n        # Conditionally modify data\n        return (datatype, data)\n\n    Where datatype is a valid ABI type string, like \"uint\".\n\n    In case of an array, like \"bool[2]\", normalizer will receive `data`\n    as an iterable of typed data, like `[(\"bool\", True), (\"bool\", False)]`.\n\n    Internals\n    ---\n\n    This is accomplished by:\n\n    1. Decorating the data tree with types\n    2. Recursively mapping each of the normalizers to the data\n    3. Stripping the types back out of the tree\n    \"\"\"\n\n    pipeline = itertools.chain(\n        [abi_data_tree(types)],\n        map(data_tree_map, normalizers),\n        [partial(recursive_map, strip_abi_type)],\n    )\n\n    return pipe(data, *pipeline)\n\n\n@curry\ndef abi_data_tree(types, data):\n    \"\"\"Decorate the data tree with pairs of (type, data). The pair tuple is actually an\n    ABITypedData, but can be accessed as a tuple.\n\n    Examples:\n        >>> abi_data_tree(types=[\"bool[2]\", \"uint\"], data=[[True, False], 0])\n\n    Returns:\n        [(\"bool[2]\", [(\"bool\", True), (\"bool\", False)]), (\"uint256\", 0)]\n    \"\"\"\n\n    return [\n        abi_sub_tree(data_type, data_value)\n        for data_type, data_value\n        in zip(types, data)\n    ]\n\n\n@curry\ndef data_tree_map(func, data_tree):\n    \"\"\"\n    Map func to every ABITypedData element in the tree. func will\n    receive two args: abi_type, and data\n    \"\"\"\n\n    def map_to_typed_data(elements):\n        if isinstance(elements, ABITypedData) and elements.abi_type is not None:\n            return ABITypedData(func(*elements))\n        else:\n            return elements\n\n    return recursive_map(map_to_typed_data, data_tree)\n\n\nclass ABITypedData(namedtuple('ABITypedData', 'abi_type, data')):\n    \"\"\"\n    This class marks data as having a certain ABI-type.\n\n    >>> a1 = ABITypedData(['address', addr1])\n    >>> a2 = ABITypedData(['address', addr2])\n    >>> addrs = ABITypedData(['address[]', [a1, a2])\n\n    You can access the fields using tuple() interface, or with\n    attributes:\n\n    >>> assert a1.abi_type == a1[0]\n    >>> assert a1.data == a1[1]\n\n    Unlike a typical `namedtuple`, you initialize with a single\n    positional argument that is iterable, to match the init\n    interface of all other relevant collections.\n    \"\"\"\n\n    def __new__(cls, iterable):\n        return super().__new__(cls, *iterable)\n\n\ndef strip_abi_type(elements):\n    if isinstance(elements, ABITypedData):\n        return elements.data\n    else:\n        return elements\n"
  },
  {
    "path": "tronapi/common/account.py",
    "content": "# --------------------------------------------------------------------\n# Copyright (c) iEXBase. All rights reserved.\n# Licensed under the MIT License.\n# See License.txt in the project root for license information.\n# --------------------------------------------------------------------\n\nimport codecs\nfrom binascii import unhexlify\n\nimport base58\nimport ecdsa\nfrom eth_keys import KeyAPI\nfrom eth_account import Account as ETHAccount\nfrom trx_utils import is_hex, is_bytes\n\nfrom tronapi.common.datastructures import AttributeDict\n\n\nclass Account:\n    @staticmethod\n    def create():\n        generate_key = ecdsa.SigningKey.generate(curve=ecdsa.SECP256k1)\n        return PrivateKey(generate_key.to_string().hex())\n\n    @staticmethod\n    def sign_hash(message_hash, private_key):\n        if not is_hex(message_hash):\n            raise ValueError('Invalid message_hash provided')\n\n        return ETHAccount.signHash(message_hash, private_key)\n\n    @staticmethod\n    def recover_hash(message_hash, signature):\n        if not is_hex(message_hash):\n            raise ValueError('Invalid message_hash provided')\n\n        return ETHAccount.recoverHash(message_hash, signature=signature)\n\n\nclass Address(object):\n    @staticmethod\n    def from_hex(address):\n        \"\"\"Helper function that will convert a generic value from hex\"\"\"\n        if not is_hex(address):\n            return address\n\n        return base58.b58encode_check(bytes.fromhex(address))\n\n    @staticmethod\n    def to_hex(address):\n        \"\"\"Helper function that will convert a generic value to hex\"\"\"\n        if is_hex(address):\n            return address.lower().replace('0x', '41', 2)\n\n        return base58.b58decode_check(address).hex().upper()\n\n    @staticmethod\n    def from_private_key(private_key):\n        return PrivateKey(private_key).address\n\n\nclass PrivateKey(object):\n    def __init__(self, private_key):\n        \"\"\"Work with private key.\n        Getting: PublicKey, PublicToAddress\n\n        Example:::\n            PrivateKey(\"4d1bc37b069b9f2e975c37770b7c87185dc3a10454e3ea024ce1fce8f3eb78bf\")\n        \"\"\"\n        _private = unhexlify(bytes(private_key, encoding='utf8'))\n        self._key = KeyAPI.PrivateKey(_private)\n        _length = len(self._key)\n\n        # Key length must not exceed 64 length\n        if _length < 64:\n            raise ValueError('Key length must not exceed 64 length')\n\n    @property\n    def private_key(self):\n        _raw_key = self._key.to_bytes()\n        return codecs.decode(codecs.encode(_raw_key, 'hex'), 'ascii')\n\n    @property\n    def public_key(self) -> str:\n        public_key = self._key.public_key\n        return '04' + str(public_key)[2:]\n\n    @property\n    def address(self):\n        public_key = self._key.public_key\n        address = '41' + public_key.to_address()[2:]\n        to_base58 = base58.b58encode_check(bytes.fromhex(address))\n\n        # If bytecode then convert to string\n        if is_bytes(to_base58):\n            to_base58 = to_base58.decode()\n\n        return AttributeDict({\n            'hex': address,\n            'base58': to_base58\n        })\n\n    def __str__(self):\n        return self.private_key\n\n    def __bytes__(self):\n        return self._key.to_bytes()\n"
  },
  {
    "path": "tronapi/common/blocks.py",
    "content": "# --------------------------------------------------------------------------------------------\n# Copyright (c) iEXBase. All rights reserved.\n# Licensed under the MIT License. See License.txt in the project root for license information.\n# --------------------------------------------------------------------------------------------\nfrom trx_utils import (\n    is_string,\n    remove_0x_prefix,\n    is_hex,\n    is_integer\n)\n\n\ndef is_hex_encoded_block_hash(value):\n    if not is_string(value):\n        return False\n    return len(remove_0x_prefix(value)) == 64 and is_hex(value)\n\n\ndef is_hex_encoded_block_number(value):\n    if not is_string(value):\n        return False\n    elif is_hex_encoded_block_hash(value):\n        return False\n    try:\n        value_as_int = int(value, 16)\n    except ValueError:\n        return False\n    return 0 <= value_as_int < 2**256\n\n\ndef select_method_for_block(value, if_hash, if_number):\n    if isinstance(value, bytes):\n        return if_hash\n    elif is_hex_encoded_block_hash(value):\n        return if_hash\n    elif is_integer(value) and (0 <= value < 2**256):\n        return if_number\n    elif is_hex_encoded_block_number(value):\n        return if_number\n    else:\n        raise ValueError(\n            \"Value did not match any of the recognized block identifiers: {0}\".format(value)\n        )\n"
  },
  {
    "path": "tronapi/common/contracts.py",
    "content": "# --------------------------------------------------------------------\n# Copyright (c) iEXBase. All rights reserved.\n# Licensed under the MIT License.\n# See License.txt in the project root for license information.\n# --------------------------------------------------------------------\n\nimport functools\n\nfrom hexbytes import HexBytes\n\nfrom eth_utils import (\n    to_hex,\n    function_abi_to_4byte_selector\n)\nfrom trx_utils import is_text, encode_hex\n\nfrom tronapi.common.abi import (\n    filter_by_name,\n    filter_by_encodability,\n    filter_by_argument_count,\n    get_fallback_func_abi,\n    abi_to_signature,\n    get_abi_input_types,\n    check_if_arguments_can_be_encoded,\n    map_abi_data,\n    merge_args_and_kwargs\n)\n\nfrom tronapi.common.normalizers import (\n    abi_address_to_hex,\n    abi_bytes_to_bytes,\n    abi_string_to_text\n)\nfrom tronapi.common.toolz import (\n    pipe,\n    valmap,\n)\nfrom eth_abi import (\n    encode_abi as eth_abi_encode_abi,\n)\n\nfrom eth_abi.exceptions import (\n    EncodingError,\n)\n\n\nclass FallbackFn:\n    pass\n\n\ndef find_matching_fn_abi(abi, fn_identifier=None, args=None, kwargs=None):\n    args = args or tuple()\n    kwargs = kwargs or dict()\n    filters = []\n    num_arguments = len(args) + len(kwargs)\n    diagnosis = None\n\n    if fn_identifier is FallbackFn:\n        return get_fallback_func_abi(abi)\n\n    if not is_text(fn_identifier):\n        raise TypeError(\"Unsupported function identifier\")\n\n    name_filter = functools.partial(filter_by_name, fn_identifier)\n    arg_count_filter = functools.partial(filter_by_argument_count, num_arguments)\n    encoding_filter = functools.partial(filter_by_encodability, args, kwargs)\n    filters.extend([\n        name_filter,\n        arg_count_filter,\n        encoding_filter,\n    ])\n    function_candidates = pipe(abi, *filters)\n\n    if len(function_candidates) == 1:\n        return function_candidates[0]\n    else:\n        matching_identifiers = name_filter(abi)\n        matching_function_signatures = [abi_to_signature(func) for func in matching_identifiers]\n\n        arg_count_matches = len(arg_count_filter(matching_identifiers))\n        encoding_matches = len(encoding_filter(matching_identifiers))\n\n        if arg_count_matches == 0:\n            diagnosis = \"\\nFunction invocation failed due to improper number of arguments.\"\n        elif encoding_matches == 0:\n            diagnosis = \"\\nFunction invocation failed due to no matching argument types.\"\n        elif encoding_matches > 1:\n            diagnosis = (\n                \"\\nAmbiguous argument encoding. \"\n                \"Provided arguments can be encoded to multiple functions matching this call.\"\n            )\n        message = (\n            \"\\nCould not identify the intended function with name `{name}`, \"\n            \"positional argument(s) of type `{arg_types}` and \"\n            \"keyword argument(s) of type `{kwarg_types}`.\"\n            \"\\nFound {num_candidates} function(s) with the name `{name}`: {candidates}\"\n            \"{diagnosis}\"\n        ).format(\n            name=fn_identifier,\n            arg_types=tuple(map(type, args)),\n            kwarg_types=valmap(type, kwargs),\n            num_candidates=len(matching_identifiers),\n            candidates=matching_function_signatures,\n            diagnosis=diagnosis,\n        )\n        raise ValueError(message)\n\n\ndef encode_abi(tron, abi, arguments, data=None):\n    argument_types = get_abi_input_types(abi)\n\n    if not check_if_arguments_can_be_encoded(abi, arguments, {}):\n        raise TypeError(\n            \"One or more arguments could not be encoded to the necessary \"\n            \"ABI type.  Expected types are: {0}\".format(\n                ', '.join(argument_types),\n            )\n        )\n\n    try:\n        normalizers = [\n            abi_address_to_hex,\n            abi_bytes_to_bytes,\n            abi_string_to_text,\n        ]\n\n        normalized_arguments = map_abi_data(\n            normalizers,\n            argument_types,\n            arguments,\n        )\n\n        encoded_arguments = eth_abi_encode_abi(\n            argument_types,\n            normalized_arguments,\n        )\n    except EncodingError as e:\n        raise TypeError(\n            \"One or more arguments could not be encoded to the necessary \"\n            \"ABI type: {0}\".format(str(e))\n        )\n\n    if data:\n        return to_hex(HexBytes(data) + encoded_arguments)\n    else:\n        return encode_hex(encoded_arguments)\n\n\ndef get_function_info(fn_name, contract_abi=None, fn_abi=None, args=None, kwargs=None):\n    if args is None:\n        args = tuple()\n    if kwargs is None:\n        kwargs = {}\n\n    if fn_abi is None:\n        fn_abi = find_matching_fn_abi(contract_abi, fn_name, args, kwargs)\n\n    fn_selector = encode_hex(function_abi_to_4byte_selector(fn_abi))\n\n    fn_arguments = merge_args_and_kwargs(fn_abi, args, kwargs)\n\n    return fn_abi, fn_selector, fn_arguments\n"
  },
  {
    "path": "tronapi/common/datastructures.py",
    "content": "from collections import (\n    Hashable,\n    Mapping,\n    MutableMapping,\n    OrderedDict,\n    Sequence,\n)\n\nfrom trx_utils import (\n    is_integer,\n)\n\nfrom tronapi.common.formatters import (\n    recursive_map,\n)\n\n# Hashable must be immutable:\n# \"the implementation of hashable collections requires that a key's hash value is immutable\"\n# https://docs.python.org/3/reference/datamodel.html#object.__hash__\n\n\nclass ReadableAttributeDict(Mapping):\n    \"\"\"\n    The read attributes for the AttributeDict types\n    \"\"\"\n\n    def __init__(self, dictionary, *args, **kwargs):\n        self.__dict__ = dict(dictionary)\n        self.__dict__.update(dict(*args, **kwargs))\n\n    def __getitem__(self, key):\n        return self.__dict__[key]\n\n    def __iter__(self):\n        return iter(self.__dict__)\n\n    def __len__(self):\n        return len(self.__dict__)\n\n    def __repr__(self):\n        return self.__class__.__name__ + \"(%r)\" % self.__dict__\n\n    def _repr_pretty_(self, builder, cycle):\n        \"\"\"\n        Custom pretty output for the IPython console\n        \"\"\"\n        builder.text(self.__class__.__name__ + \"(\")\n        if cycle:\n            builder.text(\"<cycle>\")\n        else:\n            builder.pretty(self.__dict__)\n        builder.text(\")\")\n\n    @classmethod\n    def _apply_if_mapping(cls, value):\n        if isinstance(value, Mapping):\n            return cls(value)\n        else:\n            return value\n\n    @classmethod\n    def recursive(cls, value):\n        return recursive_map(cls._apply_if_mapping, value)\n\n\nclass MutableAttributeDict(MutableMapping, ReadableAttributeDict):\n\n    def __setitem__(self, key, val):\n        self.__dict__[key] = val\n\n    def __delitem__(self, key):\n        del self.__dict__[key]\n\n\nclass AttributeDict(ReadableAttributeDict, Hashable):\n    \"\"\"\n    This provides superficial immutability, someone could hack around it\n    \"\"\"\n\n    def __setattr__(self, attr, val):\n        if attr == '__dict__':\n            super().__setattr__(attr, val)\n        else:\n            raise TypeError('This data is immutable -- create a copy instead of modifying')\n\n    def __delattr__(self, key):\n        raise TypeError('This data is immutable -- create a copy instead of modifying')\n\n    def __hash__(self):\n        return hash(tuple(sorted(self.items())))\n\n    def __eq__(self, other):\n        if isinstance(other, Mapping):\n            return self.__dict__ == dict(other)\n        else:\n            return False\n\n\nclass NamedElementOnion(Mapping):\n    \"\"\"\n    Add layers to an onion-shaped structure. Optionally, inject to a specific layer.\n    This structure is iterable, where the outermost layer is first, and innermost is last.\n    \"\"\"\n    def __init__(self, init_elements, valid_element=callable):\n        self._queue = OrderedDict()\n        for element in reversed(init_elements):\n            if valid_element(element):\n                self.add(element)\n            else:\n                self.add(*element)\n\n    def add(self, element, name=None):\n        if name is None:\n            name = element\n\n        if name in self._queue:\n            if name is element:\n                raise ValueError(\"You can't add the same un-named instance twice\")\n            else:\n                raise ValueError(\"You can't add the same name again, use replace instead\")\n\n        self._queue[name] = element\n\n    def inject(self, element, name=None, layer=None):\n        \"\"\"\n        Inject a named element to an arbitrary layer in the onion.\n\n        The current implementation only supports insertion at the innermost layer,\n        or at the outermost layer. Note that inserting to the outermost is equivalent\n        to calling :meth:`add` .\n        \"\"\"\n        if not is_integer(layer):\n            raise TypeError(\"The layer for insertion must be an int.\")\n        elif layer != 0 and layer != len(self._queue):\n            raise NotImplementedError(\n                \"You can only insert to the beginning or end of a %s, currently. \"\n                \"You tried to insert to %d, but only 0 and %d are permitted. \" % (\n                    type(self),\n                    layer,\n                    len(self._queue),\n                )\n            )\n\n        self.add(element, name=name)\n\n        if layer == 0:\n            if name is None:\n                name = element\n            self._queue.move_to_end(name, last=False)\n        elif layer == len(self._queue):\n            return\n        else:\n            raise AssertionError(\"Impossible to reach: earlier validation raises an error\")\n\n    def clear(self):\n        self._queue.clear()\n\n    def replace(self, old, new):\n        if old not in self._queue:\n            raise ValueError(\"You can't replace unless one already exists, use add instead\")\n        to_be_replaced = self._queue[old]\n        if to_be_replaced is old:\n            # re-insert with new name in old slot\n            self._replace_with_new_name(old, new)\n        else:\n            self._queue[old] = new\n        return to_be_replaced\n\n    def remove(self, old):\n        if old not in self._queue:\n            raise ValueError(\"You can only remove something that has been added\")\n        del self._queue[old]\n\n    def _replace_with_new_name(self, old, new):\n        self._queue[new] = new\n        found_old = False\n        for key in list(self._queue.keys()):\n            if not found_old:\n                if key == old:\n                    found_old = True\n                continue\n            elif key != new:\n                self._queue.move_to_end(key)\n        del self._queue[old]\n\n    def __iter__(self):\n        elements = self._queue.values()\n        if not isinstance(elements, Sequence):\n            elements = list(elements)\n        return iter(reversed(elements))\n\n    def __add__(self, other):\n        if not isinstance(other, NamedElementOnion):\n            raise NotImplementedError(\"You can only combine with another NamedElementOnion\")\n        combined = self._queue.copy()\n        combined.update(other._queue)\n        return NamedElementOnion(combined.items())\n\n    def __contains__(self, element):\n        return element in self._queue\n\n    def __getitem__(self, element):\n        return self._queue[element]\n\n    def __len__(self):\n        return len(self._queue)\n\n    def __reversed__(self):\n        elements = self._queue.values()\n        if not isinstance(elements, Sequence):\n            elements = list(elements)\n        return iter(elements)\n"
  },
  {
    "path": "tronapi/common/datatypes.py",
    "content": "# --------------------------------------------------------------------\n# Copyright (c) iEXBase. All rights reserved.\n# Licensed under the MIT License.\n# See License.txt in the project root for license information.\n# --------------------------------------------------------------------\n\nfrom tronapi.common.formatters import apply_formatters_to_dict\nfrom tronapi.common.toolz import (\n    concat,\n    curry,\n)\n\n\n@curry\ndef verify_attr(class_name, key, namespace):\n    if key not in namespace:\n        raise AttributeError(\n            \"Property {0} not found on {1} class. \"\n            \"`{1}.factory` only accepts keyword arguments which are \"\n            \"present on the {1} class\".format(key, class_name)\n        )\n\n\nclass PropertyCheckingFactory(type):\n    def __init__(cls, name, bases, namespace, **kargs):\n        # see PEP487.  To accept kwargs in __new__, they need to be\n        # filtered out here.\n        super().__init__(name, bases, namespace)\n\n    def __new__(mcs, name, bases, namespace, normalizers=None):\n        all_bases = set(concat(base.__mro__ for base in bases))\n        for key in namespace:\n            verify_key_attr = verify_attr(name, key)\n            verify_key_attr(concat(base.__dict__.keys() for base in all_bases))\n\n        if normalizers:\n            processed_namespace = apply_formatters_to_dict(\n                normalizers,\n                namespace)\n        else:\n            processed_namespace = namespace\n\n        return super().__new__(mcs, name, bases, processed_namespace)\n"
  },
  {
    "path": "tronapi/common/encoding.py",
    "content": "import json\nimport re\nfrom typing import Union\n\nfrom eth_account.datastructures import AttributeDict\nfrom hexbytes import HexBytes\n\nfrom eth_utils import (\n    hexstr_if_str,\n    to_hex,\n    big_endian_to_int,\n    int_to_big_endian\n)\nfrom trx_utils import (\n    remove_0x_prefix,\n    encode_hex,\n    add_0x_prefix,\n    decode_hex,\n    is_boolean,\n    is_integer,\n    is_list_like,\n    is_bytes\n)\n\nfrom tronapi.common.abi import (\n    size_of_type,\n    sub_type_of_array_type,\n    is_array_type,\n    is_bool_type,\n    is_uint_type,\n    is_int_type,\n    is_address_type,\n    is_bytes_type,\n    is_string_type\n)\n\n\nfrom tronapi.common.validation import (\n    validate_abi_type,\n    validate_abi_value\n)\n\nfrom tronapi.common.toolz import (\n    curry\n)\n\nfrom tronapi.common.validation import assert_one_val\n\n\ndef hex_encode_abi_type(abi_type, value, force_size=None):\n    \"\"\"\n    Encodes value into a hex string in format of abi_type\n    \"\"\"\n    validate_abi_type(abi_type)\n    validate_abi_value(abi_type, value)\n\n    data_size = force_size or size_of_type(abi_type)\n    if is_array_type(abi_type):\n        sub_type = sub_type_of_array_type(abi_type)\n        return \"\".join([remove_0x_prefix(hex_encode_abi_type(sub_type, v, 256)) for v in value])\n    elif is_bool_type(abi_type):\n        return to_hex_with_size(value, data_size)\n    elif is_uint_type(abi_type):\n        return to_hex_with_size(value, data_size)\n    elif is_int_type(abi_type):\n        return to_hex_twos_compliment(value, data_size)\n    elif is_address_type(abi_type):\n        return pad_hex(value, data_size)\n    elif is_bytes_type(abi_type):\n        if is_bytes(value):\n            return encode_hex(value)\n        else:\n            return value\n    elif is_string_type(abi_type):\n        return to_hex(text=value)\n    else:\n        raise ValueError(\n            \"Unsupported ABI type: {0}\".format(abi_type)\n        )\n\n\ndef to_hex_twos_compliment(value, bit_size):\n    \"\"\"\n    Converts integer value to twos compliment hex representation with given bit_size\n    \"\"\"\n    if value >= 0:\n        return to_hex_with_size(value, bit_size)\n\n    value = (1 << bit_size) + value\n    hex_value = hex(value)\n    hex_value = hex_value.rstrip(\"L\")\n    return hex_value\n\n\ndef to_hex_with_size(value, bit_size):\n    \"\"\"Converts a value to hex with given bit_size:\"\"\"\n    return pad_hex(to_hex(value), bit_size)\n\n\ndef pad_hex(value, bit_size):\n    \"\"\"Pads a hex string up to the given bit_size\"\"\"\n    value = remove_0x_prefix(value)\n    return add_0x_prefix(value.zfill(int(bit_size / 4)))\n\n\ndef trim_hex(hexstr):\n    if hexstr.startswith('0x0'):\n        hexstr = re.sub('^0x0+', '0x', hexstr)\n        if hexstr == '0x':\n            hexstr = '0x0'\n    return hexstr\n\n\ndef to_int(value=None, hexstr=None, text=None):\n    \"\"\"Converts value to it's integer representation.\n\n    Values are converted this way:\n\n     * value:\n       * bytes: big-endian integer\n       * bool: True => 1, False => 0\n     * hexstr: interpret hex as integer\n     * text: interpret as string of digits, like '12' => 12\n    \"\"\"\n    assert_one_val(value, hexstr=hexstr, text=text)\n\n    if hexstr is not None:\n        return int(hexstr, 16)\n    elif text is not None:\n        return int(text)\n    elif isinstance(value, bytes):\n        return big_endian_to_int(value)\n    elif isinstance(value, str):\n        raise TypeError(\"Pass in strings with keyword hexstr or text\")\n    else:\n        return int(value)\n\n\n@curry\ndef text_if_str(to_type, text_or_primitive):\n    \"\"\"Convert to a type, assuming that strings can be only unicode text (not a hexstr)\"\"\"\n    if isinstance(text_or_primitive, str):\n        (primitive, text) = (None, text_or_primitive)\n    else:\n        (primitive, text) = (text_or_primitive, None)\n    return to_type(primitive, text=text)\n\n\ndef to_text(primitive=None, hexstr=None, text=None):\n    assert_one_val(primitive, hexstr=hexstr, text=text)\n\n    if hexstr is not None:\n        return to_bytes(hexstr=hexstr).decode('utf-8')\n    elif text is not None:\n        return text\n    elif isinstance(primitive, str):\n        return to_text(hexstr=primitive)\n    elif isinstance(primitive, bytes):\n        return primitive.decode('utf-8')\n    elif is_integer(primitive):\n        byte_encoding = int_to_big_endian(primitive)\n        return to_text(byte_encoding)\n    raise TypeError(\"Expected an int, bytes or hexstr.\")\n\n\ndef to_bytes(primitive=None, hexstr=None, text=None):\n    assert_one_val(primitive, hexstr=hexstr, text=text)\n\n    if is_boolean(primitive):\n        return b'\\x01' if primitive else b'\\x00'\n    elif isinstance(primitive, bytes):\n        return primitive\n    elif is_integer(primitive):\n        return to_bytes(hexstr=to_hex(primitive))\n    elif hexstr is not None:\n        if len(hexstr) % 2:\n            hexstr = '0x0' + remove_0x_prefix(hexstr)\n        return decode_hex(hexstr)\n    elif text is not None:\n        return text.encode('utf-8')\n    raise TypeError(\"expected an int in first arg, or keyword of hexstr or text\")\n\n\ndef to_4byte_hex(hex_or_str_or_bytes: Union[int, str, bytes]) -> str:\n    size_of_4bytes = 4 * 8\n    byte_str = hexstr_if_str(to_bytes, hex_or_str_or_bytes)\n    if len(byte_str) > 4:\n        raise ValueError(\n            'expected value of size 4 bytes. Got: %d bytes' % len(byte_str)\n        )\n    hex_str = encode_hex(byte_str)\n    return pad_hex(hex_str, size_of_4bytes)\n\n\nclass FriendlyJsonSerialize:\n    \"\"\"\n    Friendly JSON serializer & deserializer\n    When encoding or decoding fails, this class collects\n    information on which fields failed, to show more\n    helpful information in the raised error messages.\n    \"\"\"\n\n    def _json_mapping_errors(self, mapping):\n        for key, val in mapping.items():\n            try:\n                self._friendly_json_encode(val)\n            except TypeError as exc:\n                yield \"%r: because (%s)\" % (key, exc)\n\n    def _json_list_errors(self, iterable):\n        for index, element in enumerate(iterable):\n            try:\n                self._friendly_json_encode(element)\n            except TypeError as exc:\n                yield \"%d: because (%s)\" % (index, exc)\n\n    def _friendly_json_encode(self, obj, cls=None):\n        try:\n            encoded = json.dumps(obj, cls=cls)\n            return encoded\n        except TypeError as full_exception:\n            if hasattr(obj, 'items'):\n                item_errors = '; '.join(self._json_mapping_errors(obj))\n                raise TypeError(\"dict had unencodable value at keys: {{{}}}\".format(item_errors))\n            elif is_list_like(obj):\n                element_errors = '; '.join(self._json_list_errors(obj))\n                raise TypeError(\"list had unencodable value at index: [{}]\".format(element_errors))\n            else:\n                raise full_exception\n\n    @staticmethod\n    def json_decode(json_str):\n        try:\n            decoded = json.loads(json_str)\n            return decoded\n        except json.decoder.JSONDecodeError as exc:\n            err_msg = 'Could not decode {} because of {}.'.format(repr(json_str), exc)\n            # Calling code may rely on catching JSONDecodeError to recognize bad json\n            # so we have to re-raise the same type.\n            raise json.decoder.JSONDecodeError(err_msg, exc.doc, exc.pos)\n\n    def json_encode(self, obj, cls=None):\n        try:\n            return self._friendly_json_encode(obj, cls=cls)\n        except TypeError as exc:\n            raise TypeError(\"Could not encode to JSON: {}\".format(exc))\n\n\nclass TronJsonEncoder(json.JSONEncoder):\n    def default(self, obj):\n        if isinstance(obj, AttributeDict):\n            return {k: v for k, v in obj.items()}\n        if isinstance(obj, HexBytes):\n            return obj.hex()\n        return json.JSONEncoder.default(self, obj)\n\n\ndef to_json(obj: object) -> object:\n    \"\"\"Convert a complex object (like a transaction object) to a JSON string\"\"\"\n    return FriendlyJsonSerialize().json_encode(obj, cls=TronJsonEncoder)\n"
  },
  {
    "path": "tronapi/common/formatters.py",
    "content": "# --------------------------------------------------------------------\n# Copyright (c) iEXBase. All rights reserved.\n# Licensed under the MIT License.\n# See License.txt in the project root for license information.\n# --------------------------------------------------------------------\n\nfrom collections import Mapping, Iterable\n\nfrom eth_utils import to_dict\nfrom trx_utils import (\n    reject_recursive_repeats,\n    is_string\n)\n\nfrom tronapi.common.toolz import (\n    curry\n)\n\n\n@curry\n@to_dict\ndef apply_formatters_to_dict(formatters, value):\n    for key, item in value.items():\n        if key in formatters:\n            try:\n                yield key, formatters[key](item)\n            except (TypeError, ValueError) as exc:\n                raise type(exc)(\"Could not format value %r as field %r\" % (item, key)) from exc\n        else:\n            yield key, item\n\n\n@reject_recursive_repeats\ndef recursive_map(func, data):\n    \"\"\"\n    Apply func to data, and any collection items inside data (using map_collection).\n    Define func so that it only applies to the type of value that you want it to apply to.\n    \"\"\"\n\n    def recurse(item):\n        return recursive_map(func, item)\n\n    items_mapped = map_collection(recurse, data)\n    return func(items_mapped)\n\n\ndef map_collection(func, collection):\n    \"\"\"\n    Apply func to each element of a collection, or value of a dictionary.\n    If the value is not a collection, return it unmodified\n    \"\"\"\n    datatype = type(collection)\n    if isinstance(collection, Mapping):\n        return datatype((key, func(val)) for key, val in collection.items())\n    if is_string(collection):\n        return collection\n    elif isinstance(collection, Iterable):\n        return datatype(map(func, collection))\n    else:\n        return collection\n"
  },
  {
    "path": "tronapi/common/normalizers.py",
    "content": "import functools\nimport json\n\nfrom eth_utils import (\n    is_binary_address,\n    to_hex,\n    hexstr_if_str\n)\nfrom hexbytes import HexBytes\nfrom toolz import curry\n\nfrom tronapi.common.abi import process_type\nfrom tronapi.common.account import Address\nfrom tronapi.common.encoding import (\n    to_bytes,\n    text_if_str,\n    to_text\n)\nfrom tronapi.common.validation import (\n    validate_abi,\n    validate_address\n)\n\n\ndef implicitly_identity(to_wrap):\n    @functools.wraps(to_wrap)\n    def wrapper(abi_type, data):\n        modified = to_wrap(abi_type, data)\n        if modified is None:\n            return abi_type, data\n        else:\n            return modified\n\n    return wrapper\n\n\ndef normalize_abi(abi):\n    if isinstance(abi, str):\n        abi = json.loads(abi)\n    validate_abi(abi)\n    return abi\n\n\ndef normalize_bytecode(bytecode):\n    if bytecode:\n        bytecode = HexBytes(bytecode)\n    return bytecode\n\n\n@implicitly_identity\ndef abi_address_to_hex(abi_type, data):\n    if abi_type == 'address':\n        validate_address(data)\n        if is_binary_address(data):\n            return abi_type, to_hex(data)\n\n\n@implicitly_identity\ndef abi_string_to_text(abi_type, data):\n    if abi_type == 'string':\n        return abi_type, text_if_str(to_text, data)\n\n\n@implicitly_identity\ndef abi_bytes_to_bytes(abi_type, data):\n    base, sub, arrlist = process_type(abi_type)\n    if base == 'bytes' and not arrlist:\n        return abi_type, hexstr_if_str(to_bytes, data)\n\n\n@implicitly_identity\ndef addresses_checksummed(abi_type, data):\n    if abi_type == 'address':\n        return abi_type, to_checksum_address(data)\n\n\ndef to_checksum_address(address: str):\n    return Address().from_hex(address)\n\n\n@curry\ndef abi_resolver(abi_type, val):\n    return abi_type, val\n\n\nBASE_RETURN_NORMALIZERS = [\n    addresses_checksummed,\n]\n"
  },
  {
    "path": "tronapi/common/threads.py",
    "content": "\"\"\"\nA minimal implementation of the various gevent APIs used within this codebase.\n\"\"\"\nimport threading\nimport time\n\n\nclass Timeout(Exception):\n    \"\"\"\n    A limited subset of the `gevent.Timeout` context manager.\n    \"\"\"\n    seconds = None\n    exception = None\n    begun_at = None\n    is_running = None\n\n    def __init__(self, seconds=None, exception=None, *args, **kwargs):\n        self.seconds = seconds\n        self.exception = exception\n\n    def __enter__(self):\n        self.start()\n        return self\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        return False\n\n    def __str__(self):\n        if self.seconds is None:\n            return ''\n        return \"{0} seconds\".format(self.seconds)\n\n    @property\n    def expire_at(self):\n        if self.seconds is None:\n            raise ValueError(\"Timeouts with `seconds == None` do not have an expiration time\")\n        elif self.begun_at is None:\n            raise ValueError(\"Timeout has not been started\")\n        return self.begun_at + self.seconds\n\n    def start(self):\n        if self.is_running is not None:\n            raise ValueError(\"Timeout has already been started\")\n        self.begun_at = time.time()\n        self.is_running = True\n\n    def check(self):\n        if self.is_running is None:\n            raise ValueError(\"Timeout has not been started\")\n        elif self.is_running is False:\n            raise ValueError(\"Timeout has already been cancelled\")\n        elif self.seconds is None:\n            return\n        elif time.time() > self.expire_at:\n            self.is_running = False\n            if isinstance(self.exception, type):\n                raise self.exception(str(self))\n            elif isinstance(self.exception, Exception):\n                raise self.exception\n            else:\n                raise self\n\n    def cancel(self):\n        self.is_running = False\n\n    def sleep(self, seconds):\n        time.sleep(seconds)\n        self.check()\n\n\nclass ThreadWithReturn(threading.Thread):\n    def __init__(self, target=None, args=None, kwargs=None):\n        super().__init__(\n            target=target,\n            args=args or tuple(),\n            kwargs=kwargs or {},\n        )\n        self.target = target\n        self.args = args\n        self.kwargs = kwargs\n\n    def run(self):\n        self._return = self.target(*self.args, **self.kwargs)\n\n    def get(self, timeout=None):\n        self.join(timeout)\n        try:\n            return self._return\n        except AttributeError:\n            raise RuntimeError(\"Something went wrong.  No `_return` property was set\")\n\n\nclass TimerClass(threading.Thread):\n    def __init__(self, interval, callback, *args):\n        threading.Thread.__init__(self)\n        self.callback = callback\n        self.terminate_event = threading.Event()\n        self.interval = interval\n        self.args = args\n\n    def run(self):\n        while not self.terminate_event.is_set():\n            self.callback(*self.args)\n            self.terminate_event.wait(self.interval)\n\n    def stop(self):\n        self.terminate_event.set()\n\n\ndef spawn(target, *args, thread_class=ThreadWithReturn, **kwargs):\n    thread = thread_class(\n        target=target,\n        args=args,\n        kwargs=kwargs,\n    )\n    thread.daemon = True\n    thread.start()\n    return thread\n"
  },
  {
    "path": "tronapi/common/toolz/__init__.py",
    "content": "# --------------------------------------------------------------------\n# Copyright (c) iEXBase. All rights reserved.\n# Licensed under the MIT License.\n# See License.txt in the project root for license information.\n# --------------------------------------------------------------------\n\ntry:\n    from cytoolz import (\n        assoc,\n        complement,\n        compose,\n        concat,\n        curry,\n        dicttoolz,\n        dissoc,\n        excepts,\n        functoolz,\n        groupby,\n        identity,\n        itertoolz,\n        merge,\n        partial,\n        pipe,\n        sliding_window,\n        valfilter,\n        valmap,\n    )\nexcept ImportError:\n    from toolz import (  # noqa: F401\n        assoc,\n        complement,\n        compose,\n        concat,\n        curry,\n        dicttoolz,\n        dissoc,\n        excepts,\n        functoolz,\n        groupby,\n        identity,\n        itertoolz,\n        merge,\n        partial,\n        pipe,\n        sliding_window,\n        valfilter,\n        valmap,\n    )\n"
  },
  {
    "path": "tronapi/common/transactions.py",
    "content": "from tronapi.common.threads import Timeout\n\n\ndef wait_for_transaction_id(tron, tx_id, timeout=120, poll_latency=0.1):\n    with Timeout(timeout) as _timeout:\n        while True:\n            tx_detail = tron.trx.get_transaction(tx_id)\n            # FIXME: The check for a null `ref_block_hash` is due to parity's\n            if tx_detail is not None and \\\n                    'raw_data' in tx_detail and \\\n                    tx_detail['raw_data']['ref_block_hash'] is not None:\n                break\n            _timeout.sleep(poll_latency)\n    return tx_detail\n"
  },
  {
    "path": "tronapi/common/validation.py",
    "content": "# --------------------------------------------------------------------------------------------\n# Copyright (c) iEXBase. All rights reserved.\n# Licensed under the MIT License.\n# See License.txt in the project root for license information.\n# --------------------------------------------------------------------------------------------\nimport itertools\nimport re\nfrom _sha256 import sha256\nfrom typing import Any\n\nimport base58\nfrom eth_utils import (\n    function_abi_to_4byte_selector,\n    apply_formatter_to_array\n)\n\nfrom trx_utils import (\n    is_hex,\n    encode_hex,\n    is_0x_prefixed,\n    is_text,\n    is_list_like,\n    is_dict,\n    is_string,\n    is_bytes,\n    is_boolean,\n    is_integer,\n    is_binary_address, is_hex_address, is_checksum_address)\n\nfrom tronapi.common.toolz import (\n    compose,\n    groupby,\n    valfilter,\n    valmap,\n)\n\nfrom tronapi.common.abi import filter_by_type, abi_to_signature, is_recognized_type, is_string_type, is_bytes_type, \\\n    is_address_type, is_int_type, is_uint_type, is_bool_type, sub_type_of_array_type, is_array_type, \\\n    length_of_array_type\nfrom tronapi.exceptions import InvalidAddress\n\n\ndef _prepare_selector_collision_msg(duplicates):\n    dup_sel = valmap(apply_formatter_to_array(abi_to_signature), duplicates)\n    joined_funcs = valmap(lambda f: ', '.join(f), dup_sel)\n    func_sel_msg_list = [funcs + ' have selector ' + sel for sel, funcs in joined_funcs.items()]\n    return ' and\\n'.join(func_sel_msg_list)\n\n\ndef is_valid_url(value):\n    \"\"\"Return whether or not given value is a valid URL.\n\n    Args:\n        value(str): URL address string to validate\n    \"\"\"\n    regex = re.compile(\n        r'^(?:http|ftp)s?://'\n        r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\\.)+(?:[A-Z]{2,6}\\.?|[A-Z0-9-]{2,}\\.?)|'\n        r'localhost|'\n        r'\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|'\n        r'\\[?[A-F0-9]*:[A-F0-9:]+\\]?)'\n        r'(?::\\d+)?'\n        r'(?:/?|[/?]\\S+)$', re.IGNORECASE)\n\n    result = regex.match(value)\n    return bool(result)\n\n\ndef validate_abi(abi):\n    \"\"\"\n    Helper function for validating an ABI\n    \"\"\"\n    if not is_list_like(abi):\n        raise ValueError(\"'abi' is not a list\")\n\n    if not all(is_dict(e) for e in abi):\n        raise ValueError(\"'abi' is not a list of dictionaries\")\n\n    functions = filter_by_type('function', abi)\n    selectors = groupby(\n        compose(encode_hex, function_abi_to_4byte_selector),\n        functions\n    )\n    duplicates = valfilter(lambda funcs: len(funcs) > 1, selectors)\n    if duplicates:\n        raise ValueError(\n            'Abi contains functions with colliding selectors. '\n            'Functions {0}'.format(_prepare_selector_collision_msg(duplicates))\n        )\n\n\ndef validate_abi_type(abi_type):\n    \"\"\"\n    Helper function for validating an abi_type\n    \"\"\"\n    if not is_recognized_type(abi_type):\n        raise ValueError(\"Unrecognized abi_type: {abi_type}\".format(abi_type=abi_type))\n\n\ndef validate_abi_value(abi_type, value):\n    \"\"\"\n    Helper function for validating a value against the expected abi_type\n    Note: abi_type 'bytes' must either be python3 'bytes' object or ''\n    \"\"\"\n    if is_array_type(abi_type) and is_list_like(value):\n        # validate length\n        specified_length = length_of_array_type(abi_type)\n        if specified_length is not None:\n            if specified_length < 1:\n                raise TypeError(\n                    \"Invalid abi-type: {abi_type}. Length of fixed sized arrays\"\n                    \"must be greater than 0.\"\n                        .format(abi_type=abi_type)\n                )\n            if specified_length != len(value):\n                raise TypeError(\n                    \"The following array length does not the length specified\"\n                    \"by the abi-type, {abi_type}: {value}\"\n                        .format(abi_type=abi_type, value=value)\n                )\n\n        # validate sub_types\n        sub_type = sub_type_of_array_type(abi_type)\n        for v in value:\n            validate_abi_value(sub_type, v)\n        return\n    elif is_bool_type(abi_type) and is_boolean(value):\n        return\n    elif is_uint_type(abi_type) and is_integer(value) and value >= 0:\n        return\n    elif is_int_type(abi_type) and is_integer(value):\n        return\n    elif is_address_type(abi_type):\n        validate_address(value)\n        return\n    elif is_bytes_type(abi_type):\n        if is_bytes(value):\n            return\n        elif is_string(value):\n            if is_0x_prefixed(value):\n                return\n            else:\n                raise TypeError(\n                    \"ABI values of abi-type 'bytes' must be either\"\n                    \"a python3 'bytes' object or an '0x' prefixed string.\"\n                )\n    elif is_string_type(abi_type) and is_string(value):\n        return\n\n    raise TypeError(\n        \"The following abi value is not a '{abi_type}': {value}\".format(abi_type=abi_type, value=value)\n    )\n\n\ndef validate_address(value):\n    \"\"\"\n    Helper function for validating an address\n    \"\"\"\n    if is_bytes(value):\n        if not is_binary_address(value):\n            raise InvalidAddress(\"Address must be 20 bytes when input type is bytes\", value)\n        return\n\n    if not isinstance(value, str):\n        raise TypeError('Address {} must be provided as a string'.format(value))\n    if not is_hex_address(value):\n        raise InvalidAddress(\"Address must be 20 bytes, as a hex string with a 0x prefix\", value)\n    if not is_checksum_address(value):\n        if value == value.lower():\n            raise InvalidAddress(\n                \"Web3.py only accepts checksum addresses. \"\n                \"The software that gave you this non-checksum address should be considered unsafe, \"\n                \"please file it as a bug on their platform. \"\n                \"Try using an ENS name instead. Or, if you must accept lower safety, \"\n                \"use Web3.toChecksumAddress(lower_case_address).\",\n                value,\n            )\n        else:\n            raise InvalidAddress(\n                \"Address has an invalid EIP-55 checksum. \"\n                \"After looking up the address from the original source, try again.\",\n                value,\n            )\n\n\ndef has_one_val(*args, **kwargs):\n    vals = itertools.chain(args, kwargs.values())\n    not_nones = list(filter(lambda val: val is not None, vals))\n    return len(not_nones) == 1\n\n\ndef assert_one_val(*args, **kwargs):\n    if not has_one_val(*args, **kwargs):\n        raise TypeError(\n            \"Exactly one of the passed values can be specified. \"\n            \"Instead, values were: %r, %r\" % (args, kwargs)\n        )\n"
  },
  {
    "path": "tronapi/constants.py",
    "content": "# --------------------------------------------------------------------\n# Copyright (c) iEXBase. All rights reserved.\n# Licensed under the MIT License.\n# See License.txt in the project root for license information.\n# --------------------------------------------------------------------\n\n# Here we specify default values for the different needed urls.\n# They are verified. Don't change this unless you know what you're doing.\nDEFAULT_NODES = {\n    'full_node': 'https://api.trongrid.io',\n    'solidity_node': 'https://api.trongrid.io',\n    'event_server': 'https://api.trongrid.io'\n}\n"
  },
  {
    "path": "tronapi/contract.py",
    "content": "# --------------------------------------------------------------------\n# Copyright (c) iEXBase. All rights reserved.\n# Licensed under the MIT License.\n# See License.txt in the project root for license information.\n# --------------------------------------------------------------------\n\nimport copy\n\nfrom eth_abi import decode_abi\nfrom eth_utils import (\n    function_abi_to_4byte_selector,\n    to_hex\n)\nfrom hexbytes import HexBytes\nfrom trx_utils import (\n    encode_hex,\n    is_text,\n    deprecated_for,\n    combomethod\n)\n\nfrom tronapi.common.abi import (\n    filter_by_type,\n    merge_args_and_kwargs,\n    abi_to_signature,\n    fallback_func_abi_exists,\n    check_if_arguments_can_be_encoded,\n    map_abi_data\n)\n\nfrom tronapi.common.contracts import (\n    find_matching_fn_abi,\n    encode_abi,\n    get_function_info,\n    FallbackFn\n)\nfrom tronapi.common.datatypes import PropertyCheckingFactory\nfrom tronapi.common.encoding import to_4byte_hex\nfrom tronapi.common.normalizers import (\n    normalize_abi,\n    normalize_bytecode,\n    BASE_RETURN_NORMALIZERS\n)\nfrom tronapi.exceptions import (\n    NoABIFunctionsFound,\n    MismatchedABI,\n    FallbackNotFound\n)\n\n\nclass NonExistentFallbackFunction:\n    @staticmethod\n    def _raise_exception():\n        raise FallbackNotFound(\"No fallback function was found in the contract ABI.\")\n\n    def __getattr__(self, attr):\n        return NonExistentFallbackFunction._raise_exception\n\n\nclass ContractFunction:\n    \"\"\"Base class for contract functions\"\"\"\n    address = None\n    function_identifier = None\n    tron = None\n    contract_abi = None\n    abi = None\n    transaction = None\n    arguments = None\n\n    def __init__(self, abi=None):\n        self.abi = abi\n        self.fn_name = type(self).__name__\n\n    def __call__(self, *args, **kwargs):\n        clone = copy.copy(self)\n        if args is None:\n            clone.args = tuple()\n        else:\n            clone.args = args\n\n        if kwargs is None:\n            clone.kwargs = {}\n        else:\n            clone.kwargs = kwargs\n        clone._set_function_info()\n        return clone\n\n    def _set_function_info(self):\n        if not self.abi:\n            self.abi = find_matching_fn_abi(\n                self.contract_abi,\n                self.function_identifier,\n                self.args,\n                self.kwargs\n            )\n        if self.function_identifier is FallbackFn:\n            self.selector = encode_hex(b'')\n        elif is_text(self.function_identifier):\n            self.selector = encode_hex(function_abi_to_4byte_selector(self.abi))\n        else:\n            raise TypeError(\"Unsupported function identifier\")\n\n        self.arguments = merge_args_and_kwargs(self.abi, self.args, self.kwargs)\n\n    @classmethod\n    def factory(cls, class_name, **kwargs):\n        return PropertyCheckingFactory(class_name, (cls,), kwargs)(kwargs.get('abi'))\n\n    def __repr__(self):\n        if self.abi:\n            _repr = '<Function %s' % abi_to_signature(self.abi)\n            if self.arguments is not None:\n                _repr += ' bound to %r' % (self.arguments,)\n            return _repr + '>'\n        return '<Function %s>' % self.fn_name\n\n\nclass ContractFunctions:\n    \"\"\"Class containing contract function objects\n    \"\"\"\n\n    def __init__(self, abi, tron, address=None):\n        if abi:\n            self.abi = abi\n            self._functions = filter_by_type('function', self.abi)\n            for func in self._functions:\n                setattr(\n                    self,\n                    func['name'],\n                    ContractFunction.factory(\n                        func['name'],\n                        tron=tron,\n                        contract_abi=self.abi,\n                        address=address,\n                        function_identifier=func['name']))\n\n    def __iter__(self):\n        if not hasattr(self, '_functions') or not self._functions:\n            return\n\n        for func in self._functions:\n            yield func['name']\n\n    def __getattr__(self, function_name):\n        if '_functions' not in self.__dict__:\n            raise NoABIFunctionsFound(\n                \"The abi for this contract contains no function definitions. \",\n                \"Are you sure you provided the correct contract abi?\"\n            )\n        elif function_name not in self.__dict__['_functions']:\n            raise MismatchedABI(\n                \"The function '{}' was not found in this contract's abi. \".format(function_name),\n                \"Are you sure you provided the correct contract abi?\"\n            )\n        else:\n            return super().__getattribute__(function_name)\n\n    def __getitem__(self, function_name):\n        return getattr(self, function_name)\n\n\nclass Contract:\n    # set during class construction\n    tron = None\n\n    # instance level properties\n    address = None\n\n    # class properties (overridable at instance level)\n    abi = None\n\n    bytecode = None\n    bytecode_runtime = None\n\n    functions = None\n    events = None\n\n    def __init__(self, address=None):\n        \"\"\"Create a new smart contract proxy object.\n        :param address: Contract address as 0x hex string\n        \"\"\"\n        if self.tron is None:\n            raise AttributeError(\n                'The `Contract` class has not been initialized.  Please use the '\n                '`tron.contract` interface to create your contract class.'\n            )\n\n        if address:\n            self.address = self.tron.address.to_hex(address)\n\n        if not self.address:\n            raise TypeError(\"The address argument is required to instantiate a contract.\")\n\n        self.functions = ContractFunctions(self.abi, self.tron, self.address)\n        self.fallback = Contract.get_fallback_function(self.abi, self.tron, self.address)\n\n    @classmethod\n    def factory(cls, tron, class_name=None, **kwargs):\n\n        kwargs['tron'] = tron\n        normalizers = {\n            'abi': normalize_abi,\n            'bytecode': normalize_bytecode,\n            'bytecode_runtime': normalize_bytecode,\n        }\n\n        contract = PropertyCheckingFactory(\n            class_name or cls.__name__,\n            (cls,),\n            kwargs,\n            normalizers=normalizers\n        )\n\n        setattr(contract, 'functions', ContractFunctions(contract.abi, contract.tron))\n        setattr(contract, 'fallback', Contract.get_fallback_function(contract.abi, contract.tron))\n\n        return contract\n\n    @classmethod\n    @deprecated_for(\"contract.constructor.transact\")\n    def deploy(cls, **kwargs):\n        \"\"\"Deploy Contract\n\n        Deploys a contract.\n        Returns TransactionExtention, which contains an unsigned transaction.\n\n        Example:\n        .. code-block:: python\n            >>> MyContract.deploy(\n                fee_limit=10**9,\n                call_value=0,\n                consume_user_resource_percent=10\n            )\n\n        Args:\n            **kwargs: Transaction parameters for the deployment\n            transaction as a dict\n\n        \"\"\"\n        return cls.tron.transaction_builder.create_smart_contract(\n            **kwargs,\n            abi=cls.abi,\n            bytecode=to_hex(cls.bytecode)\n        )\n\n    @classmethod\n    def constructor(cls):\n        if cls.bytecode is None:\n            raise ValueError(\n                \"Cannot call constructor on a contract that does not have 'bytecode' associated \"\n                \"with it\"\n            )\n        return ContractConstructor(cls.tron,\n                                   cls.abi,\n                                   cls.bytecode)\n\n    @combomethod\n    def encodeABI(cls, fn_name, args=None, kwargs=None, data=None):\n        \"\"\"Encodes the arguments using the Tron ABI for the contract function\n        that matches the given name and arguments..\n        \"\"\"\n        fn_abi, fn_selector, fn_arguments = get_function_info(\n            fn_name, contract_abi=cls.abi, args=args, kwargs=kwargs,\n        )\n\n        if data is None:\n            data = fn_selector\n\n        return encode_abi(cls.tron, fn_abi, fn_arguments, data)\n\n    @staticmethod\n    def get_fallback_function(abi, tron, address=None):\n        if abi and fallback_func_abi_exists(abi):\n            return ContractFunction.factory(\n                'fallback',\n                tron=tron,\n                contract_abi=abi,\n                address=address,\n                function_identifier=FallbackFn)()\n\n        return NonExistentFallbackFunction()\n\n    @combomethod\n    def all_functions(self):\n        return find_functions_by_identifier(\n            self.abi, self.tron, self.address, lambda _: True\n        )\n\n    @combomethod\n    def get_function_by_signature(self, signature):\n        if ' ' in signature:\n            raise ValueError(\n                'Function signature should not contain any spaces. '\n                'Found spaces in input: %s' % signature\n            )\n\n        def callable_check(fn_abi):\n            return abi_to_signature(fn_abi) == signature\n\n        fns = find_functions_by_identifier(self.abi, self.tron, self.address, callable_check)\n        return get_function_by_identifier(fns, 'signature')\n\n    @combomethod\n    def find_functions_by_name(self, fn_name):\n        def callable_check(fn_abi):\n            return fn_abi['name'] == fn_name\n\n        return find_functions_by_identifier(\n            self.abi, self.tron, self.address, callable_check\n        )\n\n    @combomethod\n    def get_function_by_name(self, fn_name):\n        fns = self.find_functions_by_name(fn_name)\n        return get_function_by_identifier(fns, 'name')\n\n    @combomethod\n    def get_function_by_selector(self, selector):\n        def callable_check(fn_abi):\n            return encode_hex(function_abi_to_4byte_selector(fn_abi)) == to_4byte_hex(selector)\n\n        fns = find_functions_by_identifier(self.abi, self.tron, self.address, callable_check)\n        return get_function_by_identifier(fns, 'selector')\n\n    @combomethod\n    def decode_function_input(self, data):\n        data = HexBytes(data)\n        selector, params = data[:4], data[4:]\n        func = self.get_function_by_selector(selector)\n        names = [x['name'] for x in func.abi['inputs']]\n        types = [x['type'] for x in func.abi['inputs']]\n        decoded = decode_abi(types, params)\n        normalized = map_abi_data(BASE_RETURN_NORMALIZERS, types, decoded)\n        return func, dict(zip(names, normalized))\n\n    @combomethod\n    def find_functions_by_args(self, *args):\n        def callable_check(fn_abi):\n            return check_if_arguments_can_be_encoded(fn_abi, args=args, kwargs={})\n\n        return find_functions_by_identifier(\n            self.abi, self.tron, self.address, callable_check\n        )\n\n    @combomethod\n    def get_function_by_args(self, *args):\n        fns = self.find_functions_by_args(*args)\n        return get_function_by_identifier(fns, 'args')\n\n\nclass ContractConstructor:\n    \"\"\"\n    Class for contract constructor API.\n    \"\"\"\n\n    def __init__(self, tron, abi, bytecode):\n        self.tron = tron\n        self.abi = abi\n        self.bytecode = bytecode\n\n    @staticmethod\n    def check_forbidden_keys_in_transaction(transaction, forbidden_keys=None):\n        keys_found = set(transaction.keys()) & set(forbidden_keys)\n        if keys_found:\n            raise ValueError(\"Cannot set {} in transaction\".format(', '.join(keys_found)))\n\n    @combomethod\n    def transact(self, **kwargs):\n        \"\"\"Deploy Contract\n\n        Deploys a contract.\n        Returns TransactionExtention, which contains an unsigned transaction.\n\n        Args:\n            **kwargs: Additional options to send\n        \"\"\"\n\n        return self.tron.transaction_builder.create_smart_contract(\n            **kwargs,\n            abi=self.abi,\n            bytecode=to_hex(self.bytecode)\n        )\n\n\ndef find_functions_by_identifier(contract_abi, tron, address, callable_check):\n    fns_abi = filter_by_type('function', contract_abi)\n    return [\n        ContractFunction.factory(\n            fn_abi['name'],\n            tron=tron,\n            contract_abi=contract_abi,\n            address=address,\n            function_identifier=fn_abi['name'],\n            abi=fn_abi\n        )\n        for fn_abi in fns_abi\n        if callable_check(fn_abi)\n    ]\n\n\ndef get_function_by_identifier(fns, identifier):\n    if len(fns) > 1:\n        raise ValueError(\n            'Found multiple functions with matching {0}. '\n            'Found: {1!r}'.format(identifier, fns)\n        )\n    elif len(fns) == 0:\n        raise ValueError(\n            'Could not find any function with matching {0}'.format(identifier)\n        )\n    return fns[0]\n"
  },
  {
    "path": "tronapi/exceptions.py",
    "content": "# --------------------------------------------------------------------\n# Copyright (c) iEXBase. All rights reserved.\n# Licensed under the MIT License.\n# See License.txt in the project root for license information.\n# --------------------------------------------------------------------\n\n\nclass TronError(Exception):\n    \"\"\"Base class for TronAPI exceptions.\"\"\"\n\n\nclass InvalidTronError(TronError):\n    \"\"\"Raised Tron Error\"\"\"\n\n\nclass FallbackNotFound(Exception):\n    \"\"\"\n    Raised when fallback function doesn't exist in contract.\n    \"\"\"\n    pass\n\n\nclass MismatchedABI(Exception):\n    \"\"\"\n    Raised when an ABI does not match with supplied parameters, or when an\n    attempt is made to access a function/event that does not exist in the ABI.\n    \"\"\"\n    pass\n\n\nclass InvalidAddress(ValueError):\n    \"\"\"\n    The supplied address does not have a valid checksum, as defined in EIP-55\n    \"\"\"\n    pass\n\n\nclass NoABIFunctionsFound(AttributeError):\n    \"\"\"\n    Raised when an ABI doesn't contain any functions.\n    \"\"\"\n    pass\n\n\nclass ValidationError(Exception):\n    \"\"\"\n    Raised when a supplied value is invalid.\n    \"\"\"\n    pass\n\n\nclass TransportError(TronError):\n    \"\"\"Base exception for transport related errors.\n\n    This is mainly for cases where the status code denotes an HTTP error, and\n    for cases in which there was a connection error.\n\n    \"\"\"\n\n    @property\n    def status_code(self):\n        return self.args[0]\n\n    @property\n    def error(self):\n        return self.args[1]\n\n    @property\n    def info(self):\n        return self.args[2]\n\n    @property\n    def url(self):\n        return self.args[3]\n\n\nclass HttpError(TransportError):\n    \"\"\"Exception for errors occurring when connecting, and/or making a request\"\"\"\n\n\nclass BadRequest(TransportError):\n    \"\"\"Exception for HTTP 400 errors.\"\"\"\n\n\nclass NotFoundError(TransportError):\n    \"\"\"Exception for HTTP 404 errors.\"\"\"\n\n\nclass ServiceUnavailable(TransportError):\n    \"\"\"Exception for HTTP 503 errors.\"\"\"\n\n\nclass GatewayTimeout(TransportError):\n    \"\"\"Exception for HTTP 503 errors.\"\"\"\n\n\nclass TimeExhausted(Exception):\n    \"\"\"\n    Raised when a method has not retrieved the desired result within a specified timeout.\n    \"\"\"\n    pass\n\n\nHTTP_EXCEPTIONS = {\n    400: BadRequest,\n    404: NotFoundError,\n    503: ServiceUnavailable,\n    504: GatewayTimeout,\n}\n"
  },
  {
    "path": "tronapi/main.py",
    "content": "# -*- coding: utf-8 -*-\n# --------------------------------------------------------------------\n# Copyright (c) iEXBase. All rights reserved.\n# Licensed under the MIT License.\n# See License.txt in the project root for license information.\n# --------------------------------------------------------------------\n\n\"\"\"\n    tronapi.main\n    ===============\n\n    Connect to the Tron network.\n\n    :copyright: © 2019 by the iEXBase.\n    :license: MIT License\n\"\"\"\n\nfrom eth_account.datastructures import AttributeDict\nfrom urllib.parse import urlencode\nfrom eth_utils import (\n    apply_to_return_value,\n    to_hex,\n    keccak as tron_keccak,\n)\nfrom hexbytes import HexBytes\nfrom trx_utils import (\n    to_sun,\n    from_sun,\n    is_integer,\n    add_0x_prefix,\n    remove_0x_prefix,\n    is_address\n)\n\nfrom tronapi.common.abi import map_abi_data\n\n\nfrom tronapi.common.account import Address, PrivateKey, Account\nfrom tronapi.common.normalizers import abi_resolver\nfrom tronapi.common.encoding import (\n    to_bytes,\n    to_int,\n    to_text,\n    to_json,\n    hex_encode_abi_type\n)\n\nfrom tronapi.exceptions import (\n    InvalidTronError,\n    TronError\n)\nfrom tronapi.manager import TronManager\nfrom tronapi import HttpProvider, constants\nfrom tronapi.transactionbuilder import TransactionBuilder\nfrom tronapi.trx import Trx\n\n\nDEFAULT_MODULES = {\n    'trx': Trx\n}\n\n\nclass Tron:\n    # Providers\n    HTTPProvider = HttpProvider\n\n    _default_block = None\n    _private_key = None\n    _default_address = AttributeDict({})\n\n    # Encoding and Decoding\n    toBytes = staticmethod(to_bytes)\n    toInt = staticmethod(to_int)\n    toHex = staticmethod(to_hex)\n    toText = staticmethod(to_text)\n    toJSON = staticmethod(to_json)\n\n    # Currency Utility\n    toSun = staticmethod(to_sun)\n    fromSun = staticmethod(from_sun)\n\n    # Validate address\n    isAddress = staticmethod(is_address)\n\n    def __init__(self, **kwargs):\n        \"\"\"Connect to the Tron network.\n\n        Args:\n            kwargs (Any): We fill the most necessary parameters\n            for working with blockchain Tron\n\n        \"\"\"\n\n        # We check the obtained nodes, if the necessary parameters\n        # are not specified, then we take the default\n        kwargs.setdefault('full_node', constants.DEFAULT_NODES['full_node'])\n        kwargs.setdefault('solidity_node', constants.DEFAULT_NODES['solidity_node'])\n        kwargs.setdefault('event_server', constants.DEFAULT_NODES['event_server'])\n\n        # The node manager allows you to automatically determine the node\n        # on the router or manually refer to a specific node.\n        # solidity_node, full_node or event_server\n        self.manager = TronManager(self, dict(\n            full_node=kwargs.get('full_node'),\n            solidity_node=kwargs.get('solidity_node'),\n            event_server=kwargs.get('event_server')\n        ))\n\n        # If the parameter of the private key is not empty,\n        # then write to the variable\n        if 'private_key' in kwargs:\n            self.private_key = kwargs.get('private_key')\n\n        # We check whether the default wallet address is set when\n        # defining the class, and then written to the variable\n        if 'default_address' in kwargs:\n            self.default_address = kwargs.get('default_address')\n\n        # If custom methods are not declared,\n        # we take the default from the list\n        modules = kwargs.setdefault('modules', DEFAULT_MODULES)\n        for module_name, module_class in modules.items():\n            module_class.attach(self, module_name)\n\n        self.transaction_builder = TransactionBuilder(self)\n\n    @property\n    def default_block(self):\n        return self._default_block\n\n    @default_block.setter\n    def default_block(self, block_id):\n        \"\"\"Sets the default block used as a reference for all future calls.\"\"\"\n        if block_id in ('latest', 'earliest', 0):\n            self._default_block = block_id\n            return\n\n        if not is_integer(block_id) or not block_id:\n            raise ValueError('Invalid block ID provided')\n\n        self._default_block = abs(block_id)\n\n    @property\n    def providers(self):\n        \"\"\"List providers\"\"\"\n        return self.manager.providers\n\n    @property\n    def private_key(self):\n        \"\"\"Get a private key\"\"\"\n        return self._private_key\n\n    @private_key.setter\n    def private_key(self, value: str) -> None:\n        \"\"\"Set a private key used with the TronAPI instance,\n        used for obtaining the address, signing transactions etc...\n\n        Args:\n            value (str): Private key\n        \"\"\"\n        try:\n            private_key = PrivateKey(value)\n        except ValueError:\n            raise TronError('Invalid private key provided')\n\n        self._private_key = str(private_key).lower()\n\n    @property\n    def default_address(self) -> AttributeDict:\n        \"\"\"Get a TRON Address\"\"\"\n        return self._default_address\n\n    @default_address.setter\n    def default_address(self, address: str) -> None:\n        \"\"\"Sets the address used with all Tron API.\n        Will not sign any transactions.\n\n        Args:\n             address (str) Tron Address\n\n        \"\"\"\n\n        if not self.isAddress(address):\n            raise InvalidTronError('Invalid address provided')\n\n        _hex = self.address.to_hex(address)\n        _base58 = self.address.from_hex(address)\n        _private_base58 = self.address.from_private_key(self._private_key).base58\n\n        # check the addresses\n        if self._private_key and _private_base58 != _base58:\n            self._private_key = None\n\n        self._default_address = AttributeDict({\n            'hex': _hex,\n            'base58': _base58\n        })\n\n    def get_event_result(self, **kwargs):\n        \"\"\"Will return all events matching the filters.\n\n        Args:\n            kwargs (any): List parameters\n        \"\"\"\n\n        # Check the most necessary parameters\n        since_timestamp = kwargs.setdefault('since_timestamp', 0)\n        event_name = kwargs.setdefault('event_name', 'Notify')\n        block_number = kwargs.setdefault('block_number', '')\n        size = kwargs.setdefault('size', 20)\n        page = kwargs.setdefault('page', 1)\n        only_confirmed = kwargs.setdefault('only_confirmed', None)\n        only_unconfirmed = kwargs.setdefault('only_unconfirmed', None)\n        previous_last = kwargs.setdefault('previous_last_event_fingerprint', None)\n        contract_address = kwargs.setdefault('contract_address', self.default_address.hex)\n\n        if not self.isAddress(contract_address):\n            raise InvalidTronError('Invalid contract address provided')\n\n        if event_name and not contract_address:\n            raise TronError('Usage of event name filtering requires a contract address')\n\n        if block_number and event_name is None:\n            raise TronError('Usage of block number filtering requires an event name')\n\n        if not is_integer(page):\n            raise ValueError('Invalid size provided')\n\n        if not is_integer(since_timestamp):\n            raise ValueError('Invalid sinceTimestamp provided')\n\n        # If the size exceeds 200, displays an error\n        if size > 200:\n            raise ValueError('Defaulting to maximum accepted size: 200')\n\n        # We collect all parameters in one array\n        route_params = []\n        if contract_address:\n            route_params.append(contract_address)\n        if event_name:\n            route_params.append(event_name)\n        if block_number:\n            route_params.append(block_number)\n\n        route = '/'.join(route_params)\n\n        qs = {\n            'since': since_timestamp,\n            'page': page,\n            'size': size\n        }\n\n        if only_confirmed is not None:\n            qs.update({'onlyConfirmed': only_confirmed})\n\n        if only_unconfirmed is not None and not only_confirmed:\n            qs.update({'onlyUnconfirmed': only_unconfirmed})\n\n        if previous_last is not None:\n            qs.update({'previousLastEventFingerprint': previous_last})\n\n        return self.manager.request(\"/event/contract/{0}?{1}\"\n                                    .format(route, urlencode(qs)), method='get')\n\n    def get_event_transaction_id(self, tx_id):\n        \"\"\"Will return all events within a transactionID.\n\n        Args:\n            tx_id (str): TransactionID to query for events.\n        \"\"\"\n        response = self.manager.request('/event/transaction/' + tx_id, method='get')\n        return response\n\n    @property\n    def address(self) -> Address:\n        \"\"\"Helper object that allows you to convert\n        between hex/base58 and private key representations of a TRON address.\n\n        Note:\n            If you wish to convert generic data to hexadecimal strings,\n            please use the function tron.to_hex.\n\n        \"\"\"\n        return Address()\n\n    @property\n    def create_account(self) -> PrivateKey:\n        \"\"\"Create account\n\n        Warning: Please control risks when using this API.\n        To ensure environmental security, please do not invoke APIs\n        provided by other or invoke this very API on a public network.\n\n        \"\"\"\n        return Account.create()\n\n    @staticmethod\n    def is_valid_provider(provider) -> bool:\n        \"\"\"Check connected provider\n\n        Args:\n            provider(HttpProvider): Provider\n        \"\"\"\n        return isinstance(provider, HttpProvider)\n\n    def solidity_sha3(self, abi_types, values):\n        \"\"\"\n            Executes keccak256 exactly as Solidity does.\n            Takes list of abi_types as inputs -- `[uint24, int8[], bool]`\n            and list of corresponding values  -- `[20, [-1, 5, 0], True]`\n\n            Args:\n                abi_types (any): types abi\n                values (any): values\n\n            Examples:\n                >>> tron = Tron()\n                >>> sol = tron.solidity_sha3(['uint8[]'], [[1, 2, 3, 4, 5]])\n                >>> assert sol.hex() == '0x5917e5a395fb9b454434de59651d36822a9e29c5ec57474df3e67937b969460c'\n\n        \"\"\"\n        if len(abi_types) != len(values):\n            raise ValueError(\n                \"Length mismatch between provided abi types and values.  Got \"\n                \"{0} types and {1} values.\".format(len(abi_types), len(values))\n            )\n\n        normalized_values = map_abi_data([abi_resolver()], abi_types, values)\n\n        hex_string = add_0x_prefix(''.join(\n            remove_0x_prefix(hex_encode_abi_type(abi_type, value))\n            for abi_type, value\n            in zip(abi_types, normalized_values)\n        ))\n        return self.keccak(hexstr=hex_string)\n\n    @staticmethod\n    @apply_to_return_value(HexBytes)\n    def keccak(primitive=None, text=None, hexstr=None):\n        if isinstance(primitive, (bytes, int, type(None))):\n            input_bytes = to_bytes(primitive, hexstr=hexstr, text=text)\n            return tron_keccak(input_bytes)\n\n        raise TypeError(\n            \"You called keccak with first arg %r and keywords %r. You must call it with one of \"\n            \"these approaches: keccak(text='txt'), keccak(hexstr='0x747874'), \"\n            \"keccak(b'\\\\x74\\\\x78\\\\x74'), or keccak(0x747874).\" % (\n                primitive,\n                {'text': text, 'hexstr': hexstr}\n            )\n        )\n\n    def is_connected(self):\n        \"\"\"List of available providers\"\"\"\n        return self.manager.is_connected()\n"
  },
  {
    "path": "tronapi/manager.py",
    "content": "# --------------------------------------------------------------------\n# Copyright (c) iEXBase. All rights reserved.\n# Licensed under the MIT License.\n# See License.txt in the project root for license information.\n# --------------------------------------------------------------------\n\n\"\"\"\n    tronapi.manager\n    ===============\n\n    This class is designed to configure and\n    define nodes for different types.\n\n    :copyright: © 2019 by the iEXBase.\n    :license: MIT License\n\"\"\"\nfrom trx_utils import is_string\n\nfrom tronapi import HttpProvider\nfrom tronapi.constants import DEFAULT_NODES\n\n# In this variable, you can specify the base paths\n# to test the connection with the nodes.\n# It is advisable to leave the settings unchanged.\nSTATUS_PAGE = {\n    'full_node': '/wallet/getnowblock',\n    'solidity_node': '/walletsolidity/getnowblock',\n    'event_server': '/healthcheck'\n}\n\n\nclass TronManager(object):\n    \"\"\"This class is designed to configure and define nodes\n    for different types.\n\n    \"\"\"\n\n    _providers = None\n\n    def __init__(self, tron, providers):\n        \"\"\"Create new manager tron instance\n\n        Args:\n            tron: The tron implementation\n            providers: List of providers\n\n        \"\"\"\n        self.tron = tron\n        self.providers = providers\n        self.preferred_node = None\n\n        for key, value in self.providers.items():\n            # This condition checks the nodes,\n            # if the link to the node is not specified,\n            # we insert the default value to avoid an error.\n            if not providers[key]:\n                self.providers[key] = HttpProvider(DEFAULT_NODES[key])\n\n            # If the type of the accepted provider is lower-case,\n            # then we transform it to “HttpProvider”,\n            if is_string(value):\n                self.providers[key] = HttpProvider(value)\n            self.providers[key].status_page = STATUS_PAGE[key]\n\n    @property\n    def providers(self):\n        \"\"\"Getting a list of all providers\n\n        \"\"\"\n        return self._providers or tuple()\n\n    @providers.setter\n    def providers(self, value) -> None:\n        \"\"\"Add a new provider\n\n        \"\"\"\n        self._providers = value\n\n    @property\n    def full_node(self) -> HttpProvider:\n        \"\"\"Getting and managing paths to a full node\n\n        \"\"\"\n        if 'full_node' not in self.providers:\n            raise ValueError('Full node is not activated.')\n        return self.providers.get('full_node')\n\n    @property\n    def solidity_node(self) -> HttpProvider:\n        \"\"\"Getting and managing paths to a solidity node\n\n        \"\"\"\n        if 'solidity_node' not in self.providers:\n            raise ValueError('Solidity node is not activated.')\n        return self.providers.get('solidity_node')\n\n    @property\n    def event_server(self) -> HttpProvider:\n        \"\"\"Getting and managing paths to a event server\n\n        \"\"\"\n        if 'event_server' not in self.providers:\n            raise ValueError('Event server is not activated.')\n        return self.providers.get('event_server')\n\n    def request(self, url, params=None, method=None):\n        \"\"\"Prepare and route the request object according to the manager's configuration.\n\n        Args:\n            url (str): Path to send\n            params (dict): Options\n            method (str): Request method\n\n        \"\"\"\n        method = 'post' if method is None else method\n\n        # In this variable, we divide the resulting reference\n        # into 2 parts to determine the type of node\n        split = url[1:].split('/', 2)\n\n        if split[0] in ('walletsolidity', 'walletextension',):\n            return self.solidity_node.request(url, json=params, method=method)\n        elif split[0] in ('wallet',):\n            return self.full_node.request(url, json=params, method=method)\n        elif split[0] in ('event', 'healthcheck',):\n            return self.event_server.request(url, json=params, method=method)\n\n        raise ValueError('Could not determine the type of node')\n\n    def is_connected(self):\n        \"\"\"Check connection with providers\"\"\"\n        is_node = dict()\n        for key, value in self.providers.items():\n            is_node.update({key: value.is_connected()})\n        return is_node\n"
  },
  {
    "path": "tronapi/module.py",
    "content": "# --------------------------------------------------------------------\n# Copyright (c) iEXBase. All rights reserved.\n# Licensed under the MIT License.\n# See License.txt in the project root for license information.\n# --------------------------------------------------------------------\n\n\nclass Module:\n    \"\"\"Module Class\"\"\"\n\n    def __init__(self, tron) -> None:\n        self.tron = tron\n\n    @classmethod\n    def attach(cls, target, module_name: str = None) -> None:\n        if not module_name:\n            module_name = cls.__name__.lower()\n\n        if hasattr(target, module_name):\n            raise AttributeError(\n                \"Cannot set {0} module named '{1}'.  The Tron object \"\n                \"already has an attribute with that name\".format(\n                    target,\n                    module_name,\n                )\n            )\n\n        if isinstance(target, Module):\n            tron = target.tron\n        else:\n            tron = target\n\n        setattr(target, module_name, cls(tron))\n"
  },
  {
    "path": "tronapi/providers/__init__.py",
    "content": ""
  },
  {
    "path": "tronapi/providers/base.py",
    "content": "# --------------------------------------------------------------------\n# Copyright (c) iEXBase. All rights reserved.\n# Licensed under the MIT License.\n# See License.txt in the project root for license information.\n# --------------------------------------------------------------------\nimport platform\n\nimport tronapi\n\n\nclass BaseProvider(object):\n    _status_page = None\n\n    @property\n    def status_page(self):\n        \"\"\"Get the page to check the connection\"\"\"\n        return self._status_page\n\n    @status_page.setter\n    def status_page(self, page):\n        self._status_page = page\n\n    @staticmethod\n    def _http_default_headers():\n        \"\"\"Add default headers\"\"\"\n        return {\n            'Content-Type': 'application/json',\n            'User-Agent': BaseProvider.format_user_agent()\n        }\n\n    @staticmethod\n    def format_user_agent(name=None):\n        \"\"\"Construct a User-Agent suitable for use in client code.\n        This will identify use by the provided ``name`` (which should be on the\n        format ``dist_name/version``), TronAPI version and Python version.\n        .. versionadded:: 1.1\n        \"\"\"\n        parts = ['TronAPI/%s' % tronapi.__version__,\n                 '%s/%s' % (platform.python_implementation(),\n                            platform.python_version())]\n        if name:\n            parts.insert(0, name)\n        return ' '.join(parts)\n"
  },
  {
    "path": "tronapi/providers/http.py",
    "content": "# --------------------------------------------------------------------\n# Copyright (c) iEXBase. All rights reserved.\n# Licensed under the MIT License.\n# See License.txt in the project root for license information.\n# --------------------------------------------------------------------\n\n\n\"\"\"\n    tronapi.providers.http\n    ======================\n\n    Class for configuring http providers\n\n    :copyright: © 2018 by the iEXBase.\n    :license: MIT License\n\"\"\"\nimport logging\nfrom collections import namedtuple\nfrom urllib.parse import urlparse\n\nfrom eth_utils import to_dict\nfrom requests import Session\nfrom requests.exceptions import (\n    ConnectionError as TrxConnectionError\n)\n\nfrom tronapi.common.encoding import to_text\nfrom tronapi.providers.base import BaseProvider\nfrom tronapi.exceptions import HTTP_EXCEPTIONS, TransportError\n\nHTTP_SCHEMES = {'http', 'https'}\nHttpResponse = namedtuple('HttpResponse', ('status_code', 'headers', 'data'))\n\nlog = logging.getLogger(__name__)\n\n\nclass HttpProvider(BaseProvider):\n    \"\"\"A Connection object to make HTTP requests to a particular node.\"\"\"\n\n    def __init__(self, node_url, request_kwargs=None):\n        \"\"\"Initializes a :class:`~tronapi.providers.http.HttpProvider`\n        instance.\n\n         Args:\n            node_url (str):  Url of the node to connect to.\n            request_kwargs (dict): Optional params to send with each request.\n\n        \"\"\"\n\n        self.node_url = node_url.rstrip('/')\n        uri = urlparse(node_url)\n        # This condition checks the node that will connect\n        # to work with methods.\n        if uri.scheme not in HTTP_SCHEMES:\n            raise NotImplementedError(\n                'TronAPI does not know how to connect to scheme %r in %r' % (\n                    uri.scheme,\n                    self.node_url,\n                )\n            )\n\n        self._request_kwargs = request_kwargs or {}\n        self.session = Session()\n\n    @to_dict\n    def get_request_kwargs(self):\n        \"\"\"Header settings\"\"\"\n        if 'headers' not in self._request_kwargs:\n            yield 'headers', self._http_default_headers()\n        for key, value in self._request_kwargs.items():\n            yield key, value\n\n    def request(self, path, json=None, params=None, method=None):\n        \"\"\"Performs an HTTP request with the given parameters.\n\n           Args:\n               path (str): API endpoint path (e.g.: ``'/transactions'``).\n               json (dict): JSON data to send along with the request.\n               params (dict): Dictionary of URL (query) parameters.\n               method (str): HTTP method (e.g.: ``'GET'``).\n\n        \"\"\"\n        try:\n            response = self._request(\n                method=method,\n                url=self.node_url + path if path else self.node_url,\n                json=json,\n                params=params,\n                **self.get_request_kwargs(),\n            )\n        except TrxConnectionError as err:\n            raise err\n\n        return response.data\n\n    def is_connected(self) -> bool:\n        \"\"\"Connection check\n\n        This method sends a test request to the connected node\n        to determine its health.\n\n        Returns:\n            bool: True if successful,\n            False otherwise.\n        \"\"\"\n        response = self.request(path=self.status_page, method='get')\n        if 'blockID' in response or response == 'OK':\n            return True\n\n        return False\n\n    def _request(self, **kwargs):\n\n        kwargs.setdefault('timeout', 60)\n\n        response = self.session.request(**kwargs)\n        text = response.text\n\n        try:\n            json = response.json()\n        except ValueError:\n            json = None\n\n        if not (200 <= response.status_code < 300):\n            exc_cls = HTTP_EXCEPTIONS.get(response.status_code, TransportError)\n            raise exc_cls(response.status_code, text, json, kwargs.get('url'))\n\n        data = json if json is not None else text\n        log.debug(data)\n\n        # Additional error interceptor that will occur in case of failed requests\n        if 'Error' in data:\n            raise ValueError(data['Error'])\n\n        self.__error_manager(data)\n\n        return HttpResponse(response.status_code, response.headers, data)\n\n    @staticmethod\n    def __error_manager(data):\n        \"\"\"Manager error\n\n        Args:\n            data (any): response data\n\n        \"\"\"\n        # Additional error interceptor that will occur in case of failed requests\n        if 'Error' in data:\n            raise ValueError(data['Error'])\n\n        # Convert hash errors\n        if 'code' in data and 'message' in data:\n            data['message'] = to_text(hexstr=data['message'])\n"
  },
  {
    "path": "tronapi/transactionbuilder.py",
    "content": "# --------------------------------------------------------------------\n# Copyright (c) iEXBase. All rights reserved.\n# Licensed under the MIT License.\n# See License.txt in the project root for license information.\n# --------------------------------------------------------------------\n\nfrom datetime import datetime\nfrom typing import (\n    Any,\n    Tuple,\n    List\n)\n\nfrom eth_abi import encode_abi\nfrom trx_utils import (\n    is_string,\n    is_integer,\n    is_boolean,\n    is_hex,\n    encode_hex\n)\n\nfrom tronapi.exceptions import (\n    InvalidTronError,\n    TronError,\n    InvalidAddress\n)\nfrom tronapi.common.validation import is_valid_url\n\nDEFAULT_TIME = datetime.now()\nSTART_DATE = int(DEFAULT_TIME.timestamp() * 1000)\n\n\nclass TransactionBuilder(object):\n    def __init__(self, tron):\n        self.tron = tron\n\n    def send_transaction(self, to, amount, account=None):\n        \"\"\"Creates a transaction of transfer.\n        If the recipient address does not exist, a corresponding account will be created.\n\n        Args:\n            to (str): to address\n            amount (float): amount\n            account (str): from address\n\n        Returns:\n            Transaction contract data\n\n        \"\"\"\n\n        # If the address of the sender is not specified, we prescribe the default\n        if account is None:\n            account = self.tron.default_address.hex\n\n        if not self.tron.isAddress(to):\n            raise InvalidTronError('Invalid recipient address provided')\n\n        if not isinstance(amount, float) or amount <= 0:\n            raise InvalidTronError('Invalid amount provided')\n\n        _to = self.tron.address.to_hex(to)\n        _from = self.tron.address.to_hex(account)\n\n        if _to == _from:\n            raise TronError('Cannot transfer TRX to the same account')\n\n        response = self.tron.manager.request('/wallet/createtransaction', {\n            'to_address': _to,\n            'owner_address': _from,\n            'amount': self.tron.toSun(amount)\n        })\n\n        return response\n\n    def send_token(self, to, amount, token_id, account=None):\n        \"\"\"Transfer Token\n\n        Args:\n            to (str): is the recipient address\n            amount (int): is the amount of token to transfer. must be integer instead of float\n            token_id (any): Token Name and id\n            account: (str): is the address of the withdrawal account\n\n        Returns:\n            Token transfer Transaction raw data\n\n        \"\"\"\n\n        # If the address of the sender is not specified, we prescribe the default\n        if account is None:\n            account = self.tron.default_address.hex\n\n        if not self.tron.isAddress(to):\n            raise InvalidTronError('Invalid recipient address provided')\n\n        if not isinstance(amount, int) or amount <= 0:\n            raise InvalidTronError('Invalid amount provided')\n\n        if not token_id:\n            raise InvalidTronError('Invalid token ID provided')\n\n        if not self.tron.isAddress(account):\n            raise InvalidTronError('Invalid origin address provided')\n\n        _to = self.tron.address.to_hex(to)\n        _from = self.tron.address.to_hex(account)\n        _token_id = self.tron.toHex(text=str(token_id))\n\n        if _to == _from:\n            raise TronError('Cannot transfer TRX to the same account')\n\n        # In case if \"TRX\" is specified, we redirect to another method.\n        if is_string(token_id) and token_id.upper() == 'TRX':\n            return self.send_transaction(_to, amount, _from)\n\n        return self.tron.manager.request('/wallet/transferasset', {\n            'to_address': _to,\n            'owner_address': _from,\n            'asset_name': _token_id,\n            'amount': amount\n        })\n\n    def freeze_balance(self, amount, duration, resource, account=None):\n        \"\"\"\n        Freezes an amount of TRX.\n        Will give bandwidth OR Energy and TRON Power(voting rights)\n        to the owner of the frozen tokens.\n\n        Args:\n            amount (int): number of frozen trx\n            duration (int): duration in days to be frozen\n            resource (str): type of resource, must be either \"ENERGY\" or \"BANDWIDTH\"\n            account (str): address that is freezing trx account\n\n        \"\"\"\n\n        # If the address of the sender is not specified, we prescribe the default\n        if account is None:\n            account = self.tron.default_address.hex\n\n        if resource not in ('BANDWIDTH', 'ENERGY',):\n            raise InvalidTronError('Invalid resource provided: Expected \"BANDWIDTH\" or \"ENERGY\"')\n\n        if not is_integer(amount) or amount <= 0:\n            raise InvalidTronError('Invalid amount provided')\n\n        if not is_integer(duration) or duration < 3:\n            raise InvalidTronError('Invalid duration provided, minimum of 3 days')\n\n        if not self.tron.isAddress(account):\n            raise InvalidTronError('Invalid address provided')\n\n        response = self.tron.manager.request('/wallet/freezebalance', {\n            'owner_address': self.tron.address.to_hex(account),\n            'frozen_balance': self.tron.toSun(amount),\n            'frozen_duration': int(duration),\n            'resource': resource\n        })\n\n        if 'Error' in response:\n            raise TronError(response['Error'])\n\n        return response\n\n    def unfreeze_balance(self, resource='BANDWIDTH', account=None):\n        \"\"\"\n        Unfreeze TRX that has passed the minimum freeze duration.\n        Unfreezing will remove bandwidth and TRON Power.\n\n        Args:\n            resource (str): type of resource, must be either \"ENERGY\" or \"BANDWIDTH\"\n            account (str): address that is freezing trx account\n\n        \"\"\"\n\n        # If the address of the sender is not specified, we prescribe the default\n        if account is None:\n            account = self.tron.default_address.hex\n\n        if resource not in ('BANDWIDTH', 'ENERGY',):\n            raise InvalidTronError('Invalid resource provided: Expected \"BANDWIDTH\" or \"ENERGY\"')\n\n        if not self.tron.isAddress(account):\n            raise InvalidTronError('Invalid address provided')\n\n        response = self.tron.manager.request('/wallet/unfreezebalance', {\n            'owner_address': self.tron.address.to_hex(account),\n            'resource': resource\n        })\n\n        if 'Error' in response:\n            raise ValueError(response['Error'])\n\n        return response\n\n    def purchase_token(self, to: str, token_id: str, amount: int, buyer=None):\n        \"\"\"Purchase a Token\n        Creates an unsigned ICO token purchase transaction.\n\n        Args:\n            to (str): is the address of the Token issuer\n            token_id (str): is the name of the token\n            amount (int): is the number of tokens created\n            buyer (str): is the address of the Token owner\n\n        \"\"\"\n\n        if buyer is None:\n            buyer = self.tron.default_address.hex\n\n        if not self.tron.isAddress(to):\n            raise InvalidAddress('Invalid to address provided')\n\n        if not len(token_id):\n            raise ValueError('Invalid token ID provided')\n\n        if amount <= 0:\n            raise ValueError('Invalid amount provided')\n\n        _to = self.tron.address.to_hex(to)\n        _from = self.tron.address.to_hex(buyer)\n\n        return self.tron.manager.request('/wallet/participateassetissue', {\n            'to_address': _to,\n            'owner_address': _from,\n            'asset_name': self.tron.toHex(text=token_id),\n            'amount': int(amount)\n        })\n\n    def withdraw_block_rewards(self, address: str = None):\n        \"\"\"Withdraw block rewards\n        Creates an unsigned Super Representative award balance withdraw transaction.\n\n        Args:\n            address (str): Optional address to withdraw from.\n\n        \"\"\"\n        if not address:\n            address = self.tron.default_address.hex\n\n        if not self.tron.isAddress(address):\n            raise InvalidAddress('Invalid address provided')\n\n        return self.tron.manager.request('/wallet/withdrawbalance', {\n            'owner_address': self.tron.address.to_hex(address)\n        })\n\n    def apply_for_sr(self, url, address=None):\n        \"\"\"Apply to become a super representative\n\n        Args:\n            url (str): official website address\n            address (str): address\n\n        \"\"\"\n\n        # If the address of the sender is not specified, we prescribe the default\n        if address is None:\n            address = self.tron.default_address.hex\n\n        if not self.tron.isAddress(address):\n            raise TronError('Invalid address provided')\n\n        if not is_valid_url(url):\n            raise TronError('Invalid url provided')\n\n        return self.tron.manager.request('/wallet/createwitness', {\n            'owner_address': self.tron.address.to_hex(address),\n            'url': self.tron.toHex(text=url)\n        })\n\n    def vote(self, votes: List[Tuple[str, int]], voter_address: str = None):\n        \"\"\"Vote\n        Vote on the super representative\n\n        Args:\n            votes (dict): dictionary of SR address : vote count key-value pair\n            voter_address: voter address\n\n        Examples:\n            >>> from tronapi import Tron\n            >>> data = [\n            >>>     ('TRJpw2uqohP7FUmAEJgt57wakRn6aGQU6Z', 1)\n            >>> ]\n            >>> tron = Tron()\n            >>> tron.transaction.vote(data)\n\n        \"\"\"\n        if voter_address is None:\n            voter_address = self.tron.default_address.hex\n\n        _view_vote = []\n\n        # We create a cycle to check all the received data for voting.\n        for sr_address, vote_count in votes:\n            if not self.tron.isAddress(sr_address):\n                raise InvalidAddress(\n                    'Invalid SR address provided: ' + sr_address\n                )\n\n            if not is_integer(vote_count) or vote_count <= 0:\n                raise ValueError(\n                    'Invalid vote count provided for SR: ' + sr_address\n                )\n\n            _view_vote.append({\n                'vote_address': self.tron.address.to_hex(sr_address),\n                'vote_count': int(vote_count)\n            })\n\n        return self.tron.manager.request('/wallet/votewitnessaccount', {\n            'owner_address': self.tron.address.to_hex(voter_address),\n            'votes': _view_vote\n        })\n\n    def create_proposal(self, parameters: Any, issuer_address=None):\n        \"\"\"Creates a proposal to modify the network.\n        Can only be created by a current Super Representative.\n\n        Args:\n            parameters (Any): proposal parameters\n            issuer_address: owner address\n\n        Examples:\n            >>> from tronapi import Tron\n            >>> data = [\n            >>>     {'key': 1, 'value': 2},\n            >>>     {'key': 1, 'value': 2}\n            >>> ]\n            >>> tron = Tron()\n            >>> tron.transaction.create_proposal(data)\n\n\n        \"\"\"\n        if issuer_address is None:\n            issuer_address = self.tron.default_address.hex\n\n        if not self.tron.isAddress(issuer_address):\n            raise InvalidAddress('Invalid issuerAddress provided')\n\n        return self.tron.manager.request('/wallet/proposalcreate', {\n            'owner_address': self.tron.address.to_hex(issuer_address),\n            'parameters': parameters\n        })\n\n    def vote_proposal(self, proposal_id, has_approval, voter_address=None):\n        \"\"\"Proposal approval\n\n        Args:\n            proposal_id (int): proposal id\n            has_approval (bool): Approved\n            voter_address (str): Approve address\n\n        \"\"\"\n\n        # If the address of the sender is not specified, we prescribe the default\n        if voter_address is None:\n            voter_address = self.tron.default_address.hex\n\n        if not self.tron.isAddress(voter_address):\n            raise TronError('Invalid voter_address address provided')\n\n        if not is_integer(proposal_id) or proposal_id < 0:\n            raise TronError('Invalid proposal_id provided')\n\n        if not is_boolean(has_approval):\n            raise TronError('Invalid has_approval provided')\n\n        return self.tron.manager.request('/wallet/proposalapprove', {\n            'owner_address': self.tron.address.to_hex(voter_address),\n            'proposal_id': int(proposal_id),\n            'is_add_approval': bool(has_approval)\n        })\n\n    def delete_proposal(self, proposal_id: int, issuer_address: str = None):\n        \"\"\"Delete proposal\n\n        Args:\n            proposal_id (int): proposal id\n            issuer_address (str): delete the person's address\n\n        Results:\n            Delete the proposal's transaction\n\n        \"\"\"\n\n        # If the address of the sender is not specified, we prescribe the default\n        if issuer_address is None:\n            issuer_address = self.tron.default_address.hex\n\n        if not self.tron.isAddress(issuer_address):\n            raise InvalidTronError('Invalid issuer_address provided')\n\n        if not isinstance(proposal_id, int) or proposal_id < 0:\n            raise InvalidTronError('Invalid proposal_id provided')\n\n        return self.tron.manager.request('/wallet/proposaldelete', {\n            'owner_address': self.tron.address.to_hex(issuer_address),\n            'proposal_id': int(proposal_id)\n        })\n\n    def update_account(self, account_name, account: str = None):\n        \"\"\"Modify account name\n\n        Note: Username is allowed to edit only once.\n\n        Args:\n            account_name (str): name of the account\n            account (str): address\n\n        Returns:\n            modified Transaction Object\n\n        \"\"\"\n\n        # If the address of the sender is not specified, we prescribe the default\n        if account is None:\n            account = self.tron.default_address.hex\n\n        if not is_string(account_name):\n            raise ValueError('Name must be a string')\n\n        if not self.tron.isAddress(account):\n            raise TronError('Invalid origin address provided')\n\n        response = self.tron.manager.request('/wallet/updateaccount', {\n            'account_name': self.tron.toHex(text=account_name),\n            'owner_address': self.tron.address.to_hex(account)\n        })\n\n        return response\n\n    def create_smart_contract(self, **kwargs):\n        \"\"\"Deploy Contract\n\n        Deploys a contract.\n        Returns TransactionExtention, which contains an unsigned transaction.\n\n        Example:\n        .. code-block:: python\n            >>> from tronapi import Tron\n            >>>\n            >>> tron = Tron()\n            >>> tron.transaction_builder.create_smart_contract(\n            >>>    fee_limit=10**9,\n            >>>    call_value=0,\n            >>>    consume_user_resource_percent=10\n            >>> )\n\n        Args:\n            **kwargs: Transaction parameters for the deployment\n            transaction as a dict\n\n        \"\"\"\n\n        if 'bytecode' not in kwargs:\n            raise ValueError(\n                \"Cannot deploy a contract that does not have 'bytecode' associated \"\n                \"with it\"\n            )\n\n        # Maximum TRX consumption, measured in SUN (1 TRX = 1,000,000 SUN).\n        fee_limit = kwargs.setdefault('fee_limit', 0)\n        # The same as User Pay Ratio.\n        # The percentage of resources specified for users who use this contract.\n        # This field accepts integers between [0, 100].\n        user_fee_percentage = kwargs.setdefault('consume_user_resource_percent', 0)\n        # Amount of TRX transferred with this transaction, measured in SUN (1TRX = 1,000,000 SUN)\n        call_value = kwargs.setdefault('call_value', 0)\n        # Contract owner address, converted to a hex string\n        owner_address = kwargs.setdefault('owner_address', self.tron.default_address.hex)\n        # The max energy which will be consumed by the owner\n        # in the process of excution or creation of the contract,\n        # is an integer which should be greater than 0.\n        origin_energy_limit = kwargs.setdefault('origin_energy_limit', 10000000)\n\n        if not is_integer(user_fee_percentage) and not user_fee_percentage:\n            user_fee_percentage = 100\n\n        if not is_hex(kwargs.get('bytecode')):\n            raise ValueError('Invalid bytecode provided')\n\n        if not is_integer(fee_limit) or fee_limit <= 0 or \\\n                fee_limit > 1000000000:\n            raise ValueError('Invalid fee limit provided')\n\n        if not is_integer(call_value) or call_value < 0:\n            raise ValueError('Invalid call value provided')\n\n        if not is_integer(user_fee_percentage) or user_fee_percentage < 0 or \\\n                user_fee_percentage > 100:\n            raise ValueError('Invalid user fee percentage provided')\n\n        if not is_integer(origin_energy_limit) or origin_energy_limit < 0:\n            return ValueError('Invalid origin_energy_limit provided')\n\n        if not self.tron.isAddress(owner_address):\n            raise InvalidAddress('Invalid issuer address provided')\n\n        # We write all the results in one object\n        transaction = dict(**kwargs)\n        transaction.setdefault('owner_address', self.tron.address.to_hex(owner_address))\n\n        return self.tron.manager.request('/wallet/deploycontract',\n                                         transaction)\n\n    def trigger_smart_contract(self, **kwargs):\n        \"\"\"Trigger Smart Contract\n        Calls a function on a contract\n\n        Args:\n            **kwargs: Fill in the required parameters\n\n        Examples:\n            >>> tron = Tron()\n            >>> tron.transaction_builder.trigger_smart_contract(\n            >>>     contract_address='413c8143e98b3e2fe1b1a8fb82b34557505a752390',\n            >>>     function_selector='set(uint256,uint256)',\n            >>>     fee_limit=30000,\n            >>>     call_value=0,\n            >>>     parameters=[\n            >>>        {'type': 'int256', 'value': 1},\n            >>>        {'type': 'int256', 'value': 1}\n    ]\n)\n\n        Returns:\n            TransactionExtention, TransactionExtention contains unsigned Transaction\n        \"\"\"\n\n        contract_address = kwargs.setdefault('contract_address', None)\n        function_selector = kwargs.setdefault('function_selector', None)\n        parameters = kwargs.setdefault('parameters', [])\n        issuer_address = kwargs.setdefault('issuer_address', self.tron.default_address.hex)\n        call_value = kwargs.setdefault('call_value', 0)\n        fee_limit = kwargs.setdefault('fee_limit', 1000000000)\n        token_value = kwargs.setdefault('token_value', 0)\n        token_id = kwargs.setdefault('token_id', 0)\n\n        if not is_integer(token_value) or token_value < 0:\n            raise ValueError('Invalid options.tokenValue provided')\n\n        if not is_integer(token_id) or token_id < 0:\n            raise ValueError('Invalid options.tokenId provided')\n\n        if not self.tron.isAddress(contract_address):\n            raise InvalidAddress('Invalid contract address provided')\n\n        if not is_string(function_selector):\n            raise ValueError('Invalid function selector provided')\n\n        if not is_integer(call_value) or call_value < 0:\n            raise ValueError('Invalid call value provided')\n\n        if not is_integer(fee_limit) or fee_limit <= 0 or fee_limit > 1000000000:\n            raise ValueError('Invalid fee limit provided')\n\n        function_selector = function_selector.replace('/\\s*/g', '')\n\n        if len(parameters) > 0:\n            types = []\n            values = []\n            for abi in parameters:\n                if 'type' not in abi or not is_string(abi['type']):\n                    raise ValueError('Invalid parameter type provided: ' + abi['type'])\n\n                if abi['type'] == 'address':\n                    abi['value'] = self.tron.address.to_hex(abi['value']).replace('41', '0x', 1)\n\n                types.append(abi['type'])\n                values.append(abi['value'])\n\n            try:\n                parameters = encode_hex(encode_abi(types, values)).replace('0x', '', 2)\n            except ValueError as ex:\n                print(ex)\n\n        else:\n            parameters = ''\n\n        data = {\n            'contract_address': self.tron.address.to_hex(contract_address),\n            'owner_address': self.tron.address.to_hex(issuer_address),\n            'function_selector': function_selector,\n            'fee_limit': int(fee_limit),\n            'call_value': int(call_value),\n            'parameter': parameters\n        }\n\n        if token_value:\n            data['call_token_value'] = int(token_value)\n\n        if token_id:\n            data['token_id'] = int(token_id)\n\n        return self.tron.manager.request('/wallet/triggersmartcontract', data)\n\n    def create_trx_exchange(self,\n                            token_name: str,\n                            token_balance: int,\n                            trx_balance: int,\n                            account: str = None):\n        \"\"\"Create an exchange between a token and TRX.\n        Token Name should be a CASE SENSITIVE string.\n        Note: PLEASE VERIFY THIS ON TRONSCAN.\n\n        Args:\n            token_name (str): Token Name\n            token_balance (int): balance of the first token\n            trx_balance (int): balance of the second token\n            account (str): Owner Address\n        \"\"\"\n\n        # If the address of the sender is not specified, we prescribe the default\n        if account is None:\n            account = self.tron.default_address.hex\n\n        if not self.tron.isAddress(account):\n            raise TronError('Invalid address provided')\n\n        if token_balance <= 0 or trx_balance <= 0:\n            raise TronError('Invalid amount provided')\n\n        return self.tron.manager.request('/wallet/exchangecreate', {\n            'owner_address': self.tron.address.to_hex(account),\n            'first_token_id': self.tron.toHex(text=token_name),\n            'first_token_balance': token_balance,\n            'second_token_id': '5f',\n            'second_token_balance': trx_balance\n        })\n\n    def create_token_exchange(self,\n                              first_token_name: str,\n                              first_token_balance: int,\n                              second_token_name: str,\n                              second_token_balance: int,\n                              owner_address: str = None):\n        \"\"\"Create an exchange between a token and another token.\n        DO NOT USE THIS FOR TRX.\n        Token Names should be a CASE SENSITIVE string.\n\n        Args:\n            first_token_name (str): the id of the first token\n            first_token_balance (int): balance of the first token\n            second_token_name (str): the id of the second token\n            second_token_balance (int): balance of the second token\n            owner_address: owner address\n\n        \"\"\"\n        if owner_address is None:\n            owner_address = self.tron.default_address.hex\n\n        if not self.tron.isAddress(owner_address):\n            raise InvalidAddress('Invalid address provided')\n\n        if second_token_balance <= 0 or first_token_balance <= 0:\n            raise ValueError('Invalid amount provided')\n\n        return self.tron.manager.request('/wallet/exchangecreate', {\n            'owner_address': self.tron.address.to_hex(owner_address),\n            'first_token_id': self.tron.toHex(text=first_token_name),\n            'first_token_balance': first_token_balance,\n            'second_token_id': self.tron.toHex(text=second_token_name),\n            'second_token_balance': second_token_balance\n        })\n\n    def inject_exchange_tokens(self,\n                               exchange_id: int,\n                               token_name: str,\n                               token_amount: int = 0,\n                               owner_address: str = None):\n        \"\"\"Adds tokens into a bancor style exchange.\n        Will add both tokens at market rate.\n\n        Args:\n            exchange_id (int): non-negative integer exchange id\n            token_name (str): token name\n            token_amount (int): amount of token\n            owner_address (str): token owner address in hex\n\n        \"\"\"\n        if owner_address is None:\n            owner_address = self.tron.default_address.hex\n\n        if not self.tron.isAddress(owner_address):\n            raise InvalidAddress('Invalid owner_address provided')\n\n        if exchange_id < 0:\n            raise ValueError('Invalid exchange_id provided')\n\n        if token_amount < 1:\n            raise ValueError('Invalid token_amount provided')\n\n        return self.tron.manager.request('/wallet/exchangeinject', {\n            'owner_address': self.tron.address.to_hex(owner_address),\n            'exchange_id': exchange_id,\n            'token_id': self.tron.toHex(text=token_name),\n            'quant': token_amount\n        })\n\n    def create_token(self, **kwargs):\n        \"\"\"Issue Token\n\n        Issuing a token on the TRON Protocol can be done by anyone\n        who has at least 1024 TRX in their account.\n        When a token is issued it will be shown on the token overview page.\n        Users can then participate within the issuing time and exchange their\n        TRX for tokens.After issuing the token your account will\n        receive the amount of tokens equal to the total supply.\n        When other users exchange their TRX for tokens then the tokens\n        will be withdrawn from your account and you will receive\n        TRX equal to the specified exchange rate.\n\n\n        Args:\n            **kwargs: Fill in the required parameters\n\n        Examples:\n\n            >>> start_func = datetime.now()\n            >>> start = int(start_func.timestamp() * 1000)\n            >>>\n            >>> end_func = datetime.now() + timedelta(days=2)\n            >>> end = int(end_func.timestamp() * 1000)\n            >>>\n            >>> opt = {\n            >>>     'name': 'Tron',\n            >>>     'abbreviation': 'TRX',\n            >>>     'description': 'Hello World',\n            >>>     'url': 'https://github.com',\n            >>>     'totalSupply': 25000000,\n            >>>     'frozenAmount': 1,\n            >>>     'frozenDuration': 2,\n            >>>     'freeBandwidth': 10000,\n            >>>     'freeBandwidthLimit': 10000,\n            >>>     'saleStart': start,\n            >>>     'saleEnd': end,\n            >>>     'voteScore': 1\n            >>> }\n\n        \"\"\"\n        issuer_address = kwargs.setdefault(\n            'issuer_address', self.tron.default_address.hex\n        )\n\n        if not self.tron.isAddress(issuer_address):\n            raise TronError('Invalid issuer address provided')\n\n        total_supply = kwargs.setdefault('totalSupply', 0)\n        trx_ratio = kwargs.setdefault('trxRatio', 1)\n        token_ratio = kwargs.setdefault('tokenRatio', 1)\n        sale_start = kwargs.setdefault(\n            'saleStart', START_DATE\n        )\n        free_bandwidth = kwargs.setdefault('freeBandwidth', 0)\n        free_bandwidth_limit = kwargs.setdefault('freeBandwidthLimit', 0)\n        frozen_amount = kwargs.setdefault('frozenAmount', 0)\n        frozen_duration = kwargs.setdefault('frozenDuration', 0)\n        vote_score = kwargs.setdefault('voteScore', 0)\n        precision = kwargs.setdefault('precision', 0)\n\n        if not is_string(kwargs.get('name')):\n            raise ValueError('Invalid token name provided')\n\n        if not is_string(kwargs.get('abbreviation')):\n            raise ValueError('Invalid token abbreviation provided')\n\n        if not is_integer(total_supply) or total_supply <= 0:\n            raise ValueError('Invalid supply amount provided')\n\n        if not is_integer(trx_ratio) or trx_ratio <= 0:\n            raise ValueError('TRX ratio must be a positive integer')\n\n        if not is_integer(token_ratio) or token_ratio <= 0:\n            raise ValueError('Token ratio must be a positive integer')\n\n        if not is_integer(vote_score) or vote_score <= 0:\n            raise ValueError('voteScore must be a positive integer greater than 0')\n\n        if not is_integer(precision) or precision <= 0 or precision > 6:\n            raise ValueError('precision must be a positive integer > 0 and <= 6')\n\n        if not is_integer(sale_start) or sale_start < START_DATE:\n            raise ValueError('Invalid sale start timestamp provided')\n\n        if not is_integer(kwargs.get('saleEnd')) or \\\n                kwargs.get('saleEnd') <= sale_start:\n            raise ValueError('Invalid sale end timestamp provided')\n\n        if not is_string(kwargs.get('description')):\n            raise ValueError('Invalid token description provided')\n\n        if not is_valid_url(kwargs.get('url')):\n            raise ValueError('Invalid token url provided')\n\n        if not is_integer(free_bandwidth) or free_bandwidth < 0:\n            raise ValueError('Invalid free bandwidth amount provided')\n\n        if not is_integer(free_bandwidth_limit) or free_bandwidth_limit < 0 \\\n                or (free_bandwidth and not free_bandwidth_limit):\n            raise ValueError('Invalid free bandwidth limit provided')\n\n        if not is_integer(frozen_amount) or frozen_amount < 0 \\\n                or (not frozen_duration and frozen_amount):\n            raise ValueError('Invalid frozen supply provided')\n\n        if not is_integer(frozen_duration) or frozen_duration < 0 \\\n                or (frozen_duration and not frozen_amount):\n            raise ValueError('Invalid frozen duration provided')\n\n        frozen_supply = {\n            'frozen_amount': int(frozen_amount),\n            'frozen_days': int(frozen_duration)\n        }\n\n        response = self.tron.manager.request('/wallet/createassetissue', {\n            'owner_address': self.tron.address.to_hex(issuer_address),\n            'name': self.tron.toHex(text=kwargs.get('name')),\n            'abbr': self.tron.toHex(text=kwargs.get('abbreviation')),\n            'description': self.tron.toHex(text=kwargs.get('description')),\n            'url': self.tron.toHex(text=kwargs.get('url')),\n            'total_supply': int(total_supply),\n            'trx_num': int(trx_ratio),\n            'num': int(token_ratio),\n            'start_time': int(sale_start),\n            'end_time': int(kwargs.get('saleEnd')),\n            'free_asset_net_limit': int(free_bandwidth),\n            'public_free_asset_net_limit': int(free_bandwidth_limit),\n            'frozen_supply': frozen_supply,\n            'vote_score': vote_score,\n            'precision': precision\n        })\n\n        return response\n\n    def withdraw_exchange_tokens(self,\n                                 exchange_id: int,\n                                 token_name: str,\n                                 token_amount: int = 0,\n                                 owner_address: str = None):\n        \"\"\"Withdraws tokens from a bancor style exchange.\n        Will withdraw at market rate both tokens.\n\n        Args:\n            exchange_id (int): non-negative integer exchange id\n            token_name (str): token name\n            token_amount (int): number of tokens withdraw\n            owner_address (str): owner address in hex\n\n        \"\"\"\n        if owner_address is None:\n            owner_address = self.tron.default_address.hex\n\n        if not self.tron.isAddress(owner_address):\n            raise InvalidAddress('Invalid owner_address provided')\n\n        if exchange_id < 0:\n            raise ValueError('Invalid exchange_id provided')\n\n        if token_amount < 1:\n            raise ValueError('Invalid token_amount provided')\n\n        return self.tron.manager.request('/wallet/exchangewithdraw', {\n            'owner_address': self.tron.address.to_hex(owner_address),\n            'exchange_id': exchange_id,\n            'token_id': self.tron.toHex(text=token_name),\n            'quant': token_amount\n        })\n\n    def trade_exchange_tokens(self,\n                              exchange_id: int,\n                              token_name: str,\n                              token_amount_sold: int = 0,\n                              token_amount_expected: int = 0,\n                              owner_address: str = None):\n        \"\"\"Trade tokens on a bancor style exchange.\n        Expected value is a validation and used to cap the total amt of token 2 spent.\n\n        Args:\n            exchange_id (int): non-negative integer exchange id\n            token_name (str): token name\n            token_amount_sold (int): amount f token actually sold\n            token_amount_expected (int): amount of token expected\n            owner_address (str): token owner address in hex\n\n        \"\"\"\n\n        if owner_address is None:\n            owner_address = self.tron.default_address.hex\n\n        if not self.tron.isAddress(owner_address):\n            raise InvalidAddress('Invalid owner_address provided')\n\n        if exchange_id < 0:\n            raise ValueError('Invalid exchange_id provided')\n\n        if token_amount_sold < 1:\n            raise ValueError('Invalid token_amount_sold provided')\n\n        if token_amount_expected < 1:\n            raise ValueError('Invalid token_amount_expected provided')\n\n        return self.tron.manager.request('/wallet/exchangewithdraw', {\n            'owner_address': self.tron.address.to_hex(owner_address),\n            'exchange_id': exchange_id,\n            'token_id': self.tron.toHex(text=token_name),\n            'quant': token_amount_sold,\n            'expected': token_amount_expected\n        })\n\n    def update_setting(self,\n                       contract_address,\n                       user_fee_percentage,\n                       owner_address: str = None):\n        \"\"\"Update userFeePercentage.\n\n        Args:\n            contract_address (str): the address of the contract to be modified\n            user_fee_percentage (int): the percentage of resources specified for users using this contract\n            owner_address (str): is the address of the creator\n\n        Returns:\n            Contains unsigned transaction Transaction\n        \"\"\"\n\n        if owner_address is None:\n            owner_address = self.tron.default_address.hex\n\n        if not self.tron.isAddress(owner_address):\n            raise InvalidAddress('Invalid owner_address provided')\n\n        if not self.tron.isAddress(contract_address):\n            raise InvalidAddress('Invalid contract_address provided')\n\n        if not is_integer(user_fee_percentage) or user_fee_percentage < 0 or \\\n                user_fee_percentage > 100:\n            raise ValueError('Invalid user_fee_percentage provided')\n\n        return self.tron.manager.request('wallet/updatesetting', {\n            'owner_address': self.tron.address.to_hex(owner_address),\n            'contract_address': self.tron.address.to_hex(contract_address),\n            'consume_user_resource_percent': user_fee_percentage\n        })\n\n    def update_energy_limit(self,\n                            contract_address,\n                            origin_energy_limit,\n                            owner_address: str = None):\n        \"\"\"Update energy limit.\n\n        Args:\n            contract_address (str): The address of the contract to be modified\n            origin_energy_limit (int): The maximum energy set by the creator that is created\n            owner_address (str): Is the address of the creator\n\n        Returns:\n            Contains unsigned transaction Transaction\n        \"\"\"\n\n        if owner_address is None:\n            owner_address = self.tron.default_address.hex\n\n        if not self.tron.isAddress(owner_address):\n            raise InvalidAddress('Invalid owner_address provided')\n\n        if not self.tron.isAddress(contract_address):\n            raise InvalidAddress('Invalid contractAddress provided')\n\n        if not is_integer(origin_energy_limit) or origin_energy_limit < 0 or \\\n                origin_energy_limit > 10000000:\n            raise ValueError('Invalid originEnergyLimit  provided')\n\n        return self.tron.manager.request('wallet/updateenergylimit', {\n            'owner_address': self.tron.address.to_hex(owner_address),\n            'contract_address': self.tron.address.to_hex(contract_address),\n            'origin_energy_limit': origin_energy_limit\n        })\n\n    def check_permissions(self, permissions, _type):\n        if permissions is not None:\n            if permissions['type'] != _type or \\\n                    not permissions['permission_name'] or \\\n                    not is_string(permissions['permission_name']) or \\\n                    not is_integer(permissions['threshold']) or \\\n                    permissions['threshold'] < 1 or not permissions['keys']:\n                return False\n\n        for key in permissions['key']:\n            if not self.tron.isAddress(key['address']) or \\\n                    not is_integer(key['weight']) or \\\n                    key['weight'] > permissions['threshold'] or \\\n                    key['weight'] < 1 or _type == 2 and not permissions['operations']:\n                return False\n\n        return True\n\n    def update_account_permissions(self, owner_address=None,\n                                   owner_permissions=None,\n                                   witness_permissions=None,\n                                   actives_permissions=None\n                                   ):\n        \"\"\"Role: update user permissions (for multi-signature)\n\n        Args:\n            owner_address (str): The address of the account whose permissions are to be modified\n            owner_permissions: Modified owner permission\n            witness_permissions: Modified witness permission (if it is a witness)\n            actives_permissions: Modified actives permission\n        \"\"\"\n\n        if owner_address is None:\n            owner_address = self.tron.default_address.hex\n\n        if not self.check_permissions(owner_permissions, 0):\n            raise InvalidTronError('Invalid ownerPermissions provided')\n\n        if not self.check_permissions(witness_permissions, 1):\n            raise InvalidTronError('Invalid witnessPermissions provided')\n\n        for actives_permission in actives_permissions:\n            if not self.check_permissions(actives_permission, 2):\n                raise InvalidTronError('Invalid activesPermissions provided')\n\n        data = {\n            owner_address: owner_address\n        }\n\n        if owner_permissions:\n            data['owner'] = owner_permissions\n\n        if witness_permissions:\n            data['witness'] = witness_permissions\n\n        if actives_permissions:\n            if len(actives_permissions) == 1:\n                data['actives'] = actives_permissions[0]\n            else:\n                data['actives'] = actives_permissions\n\n        return self.tron.manager.request('wallet/accountpermissionupdate', data)\n"
  },
  {
    "path": "tronapi/trx.py",
    "content": "# --------------------------------------------------------------------\n# Copyright (c) iEXBase. All rights reserved.\n# Licensed under the MIT License.\n# See License.txt in the project root for license information.\n# --------------------------------------------------------------------\n\n\"\"\"\n    tronapi.trx\n    ===============\n\n    Work with basic methods\n\n    :copyright: © 2018 by the iEXBase.\n    :license: MIT License\n\"\"\"\n\nimport math\nfrom typing import Any\n\nfrom trx_utils import is_integer, is_hex\nfrom trx_utils.types import is_object, is_string, is_list\n\nfrom tronapi.common.transactions import wait_for_transaction_id\nfrom tronapi.contract import Contract\nfrom tronapi.exceptions import InvalidTronError, TronError, TimeExhausted\nfrom tronapi.module import Module\nfrom tronapi.common.blocks import select_method_for_block\nfrom tronapi.common.toolz import (\n    assoc\n)\nfrom tronapi.common.account import Account\n\nTRX_MESSAGE_HEADER = '\\x19TRON Signed Message:\\n'\nETH_MESSAGE_HEADER = '\\x19Ethereum Signed Message:\\n'\n\n\nclass Trx(Module):\n    default_contract_factory = Contract\n\n    def get_current_block(self):\n        \"\"\"Query the latest block\"\"\"\n        return self.tron.manager.request(url='/wallet/getnowblock')\n\n    def get_confirmed_current_block(self):\n        \"\"\"Query the confirmed latest block\"\"\"\n        return self.tron.manager.request('/walletsolidity/getnowblock')\n\n    def get_block(self, block: Any = None):\n        \"\"\"Get block details using HashString or blockNumber\n\n        Args:\n            block (Any): ID or height for the block\n\n        \"\"\"\n\n        # If the block identifier is not specified,\n        # we take the default\n        if block is None:\n            block = self.tron.default_block\n\n        if block == 'latest':\n            return self.get_current_block()\n        elif block == 'earliest':\n            return self.get_block(0)\n\n        method = select_method_for_block(\n            block,\n            if_hash={'url': '/wallet/getblockbyid', 'field': 'value'},\n            if_number={'url': '/wallet/getblockbynum', 'field': 'num'},\n        )\n\n        result = self.tron.manager.request(method['url'], {\n            method['field']: block\n        })\n\n        if result:\n            return result\n        raise ValueError(\"The call to {method['url']} did not return a value.\")\n\n    def get_transaction_count_by_blocknum(self, num: int):\n        \"\"\"Query transaction's count on a specified block by height\n\n        Args:\n            num (int): block number\n        \"\"\"\n        if not is_integer(num) or num < 0:\n            raise ValueError('Invalid num provided')\n\n        return self.tron.manager.request('/wallet/gettransactioncountbyblocknum', {\n            'num': num\n        })\n\n    def get_block_transaction_count(self, block: Any):\n        \"\"\"Total number of transactions in a block\n\n        Args:\n            block (Any): Number or Hash Block\n\n        \"\"\"\n        transaction = self.get_block(block)\n        if 'transactions' not in transaction:\n            raise TronError('Parameter \"transactions\" not found')\n\n        return len(transaction)\n\n    def get_transaction_from_block(self, block: Any, index: int = 0):\n        \"\"\"Get transaction details from Block\n\n        Args:\n            block (Any): Number or Hash Block\n            index (int) Position\n\n        \"\"\"\n        if not is_integer(index) or index < 0:\n            raise InvalidTronError('Invalid transaction index provided')\n\n        transactions = self.get_block(block).get('transactions')\n        if not transactions or len(transactions) < index:\n            raise TronError('Transaction not found in block')\n\n        return transactions[index]\n\n    def wait_for_transaction_id(self,\n                                transaction_hash: str,\n                                timeout=120,\n                                poll_latency=0.2):\n        \"\"\"\n        Waits for the transaction specified by transaction_hash\n        to be included in a block, then returns its transaction receipt.\n\n        Optionally, specify a timeout in seconds.\n        If timeout elapses before the transaction is added to a block,\n        then wait_for_transaction_id() raises a Timeout exception.\n\n\n        Args:\n            transaction_hash (str): Transaction Hash\n            timeout (int): TimeOut\n            poll_latency (any):  between subsequent requests\n\n        \"\"\"\n        try:\n            if poll_latency > timeout:\n                poll_latency = timeout\n\n            return wait_for_transaction_id(self.tron, transaction_hash, timeout, poll_latency)\n        except TimeoutError:\n            raise TimeExhausted(\n                \"Transaction {} is not in the chain, after {} seconds\".format(\n                    transaction_hash,\n                    timeout,\n                )\n            )\n\n    def get_transaction(self, transaction_id: str,\n                        is_confirm: bool = False):\n        \"\"\"Query transaction based on id\n\n        Args:\n            transaction_id (str): transaction id\n            is_confirm (bool):\n        \"\"\"\n\n        method = 'walletsolidity' if is_confirm else 'wallet'\n        response = self.tron.manager.request('/{}/gettransactionbyid'.format(method), {\n            'value': transaction_id\n        })\n\n        if 'txID' not in response:\n            raise ValueError('Transaction not found')\n\n        return response\n\n    def get_account_by_id(self, account_id: str, options: object):\n        return self.get_account_info_by_id(account_id, options)\n\n    def get_account_info_by_id(self, account_id: str, options: object):\n\n        if account_id.startswith('0x'):\n            account_id = id[2:]\n\n        if 'confirmed' in options:\n            return self.tron.manager.request('/walletsolidity/getaccountbyid', {\n                'account_id': self.tron.toHex(text=account_id)\n            })\n\n        return self.tron.manager.request('/wallet/getaccountbyid', {\n            'account_id': self.tron.toHex(text=account_id)\n        })\n\n    def get_unconfirmed_account_by_id(self, account_id: str):\n\n        return self.get_account_info_by_id(account_id, {\n            'confirmed': True\n        })\n\n    def get_account_resource(self, address=None):\n        \"\"\"Query the resource information of the account\n\n        Args:\n            address (str): Address\n\n        Results:\n            Resource information of the account\n\n        \"\"\"\n\n        if address is None:\n            address = self.tron.default_address.hex\n\n        if not self.tron.isAddress(address):\n            raise InvalidTronError('Invalid address provided')\n\n        return self.tron.manager.request('/wallet/getaccountresource', {\n            'address': self.tron.address.to_hex(address)\n        })\n\n    def get_account(self, address=None):\n        \"\"\"Query information about an account\n\n        Args:\n            address (str): Address\n\n        \"\"\"\n\n        if address is None:\n            address = self.tron.default_address.hex\n\n        if not self.tron.isAddress(address):\n            raise InvalidTronError('Invalid address provided')\n\n        return self.tron.manager.request('/walletsolidity/getaccount', {\n            'address': self.tron.address.to_hex(address)\n        })\n\n    def get_balance(self, address=None, is_float=False):\n        \"\"\"Getting a balance\n\n        Args:\n            address (str): Address\n            is_float (bool): Convert to float format\n\n        \"\"\"\n        response = self.get_account(address)\n        if 'balance' not in response:\n            return 0\n\n        if is_float:\n            return self.tron.fromSun(response['balance'])\n\n        return response['balance']\n\n    def get_transactions_related(self, address, direction='all', limit=30, offset=0):\n        \"\"\"Getting data in the \"from\", \"to\" and \"all\" directions\n\n        Args:\n            address (str): Address\n            direction (str): Type direction\n            address (str): address\n            limit (int): number of transactions expected to be returned\n            offset (int): index of the starting transaction\n\n        \"\"\"\n\n        if direction not in ['from', 'to', 'all']:\n            raise InvalidTronError('Invalid direction provided: Expected \"to\", \"from\" or \"all\"')\n\n        if direction == 'all':\n            _from = self.get_transactions_related(address, 'from', limit, offset)\n            _to = self.get_transactions_related(address, 'to', limit, offset)\n\n            filter_from = [{**i, 'direction': 'from'} for i in _from]\n            filter_to = [{**i, 'direction': 'to'} for i in _to]\n\n            callback = filter_from\n            callback.extend(filter_to)\n            return callback\n\n        if address is None:\n            address = self.tron.default_address.hex\n\n        if not self.tron.isAddress(address):\n            raise InvalidTronError('Invalid address provided')\n\n        if not isinstance(limit, int) or limit < 0 or (offset and limit < 1):\n            raise InvalidTronError('Invalid limit provided')\n\n        if not isinstance(offset, int) or offset < 0:\n            raise InvalidTronError('Invalid offset provided')\n\n        path = '/walletextension/gettransactions{0}this'.format(direction)\n        response = self.tron.manager.request(path, {\n            'account': {\n                'address': self.tron.address.to_hex(address)\n            },\n            'limit': limit,\n            'offset': offset\n        }, 'get')\n\n        if 'transaction' in response:\n            return response['transaction']\n        return response\n\n    def get_transactions_to_address(self, address=None, limit=30, offset=0):\n        \"\"\"Query the list of transactions received by an address\n\n        Args:\n            address (str): address\n            limit (int): number of transactions expected to be returned\n            offset (int): index of the starting transaction\n\n        Returns:\n            Transactions list\n\n        \"\"\"\n        return self.get_transactions_related(address, 'to', limit, offset)\n\n    def get_transactions_from_address(self, address=None, limit=30, offset=0):\n        \"\"\"Query the list of transactions sent by an address\n\n        Args:\n            address (str): address\n            limit (int): number of transactions expected to be returned\n            offset (int): index of the starting transaction\n\n        Returns:\n            Transactions list\n\n        \"\"\"\n        return self.get_transactions_related(address, 'from', limit, offset)\n\n    def get_transaction_info(self, tx_id):\n        \"\"\"Query transaction fee based on id\n\n        Args:\n            tx_id (str): Transaction Id\n\n        Returns:\n            Transaction fee，block height and block creation time\n\n        \"\"\"\n        response = self.tron.manager.request('/walletsolidity/gettransactioninfobyid', {\n            'value': tx_id\n        })\n\n        return response\n\n    def get_band_width(self, address=None):\n        \"\"\"Query bandwidth information.\n\n        Args:\n            address (str): address\n\n        Returns:\n            Bandwidth information for the account.\n            If a field doesn't appear, then the corresponding value is 0.\n            {\n                \"freeNetUsed\": 557,\n                \"freeNetLimit\": 5000,\n                \"NetUsed\": 353,\n                \"NetLimit\": 5239157853,\n                \"TotalNetLimit\": 43200000000,\n                \"TotalNetWeight\": 41228\n            }\n\n        \"\"\"\n\n        if address is None:\n            address = self.tron.default_address.hex\n\n        if not self.tron.isAddress(address):\n            raise InvalidTronError('Invalid address provided')\n\n        response = self.tron.manager.request('/wallet/getaccountnet', {\n            'address': self.tron.address.to_hex(address)\n        })\n\n        free_net_limit = 0 if 'freeNetLimit' not in response else response['freeNetLimit']\n        free_net_used = 0 if 'freeNetUsed' not in response else response['freeNetUsed']\n        net_limit = 0 if 'NetLimit' not in response else response['NetLimit']\n        net_used = 0 if 'NetUsed' not in response else response['NetUsed']\n\n        return (free_net_limit - free_net_used) + (net_limit - net_used)\n\n    def get_transaction_count(self):\n        \"\"\"Count all transactions on the network\n        Note: Possible delays\n\n        Returns:\n            Total number of transactions.\n\n        \"\"\"\n        response = self.tron.manager.request('/wallet/totaltransaction')\n        return response.get('num')\n\n    def send(self, to, amount, options=None):\n        \"\"\"Send funds to the Tron account (option 2)\"\"\"\n        return self.send_transaction(to, amount, options)\n\n    def send_trx(self, to, amount, options=None):\n        \"\"\"Send funds to the Tron account (option 3)\"\"\"\n        return self.send_transaction(to, amount, options)\n\n    def send_transaction(self, to, amount, options=None):\n        \"\"\"Send an asset to another account.\n        Will create and broadcast the transaction if a private key is provided.\n\n        Args:\n            to (str): Address to send TRX to.\n            amount (float): Amount of TRX to send.\n            options (Any, optional): Options\n\n        \"\"\"\n\n        if options is None:\n            options = {}\n\n        if 'from' not in options:\n            options = assoc(options, 'from', self.tron.default_address.hex)\n\n        tx = self.tron.transaction_builder.send_transaction(\n            to,\n            amount,\n            options['from']\n        )\n        # If a comment is attached to the transaction,\n        # in this case adding to the object\n        if 'message' in options:\n            tx['raw_data']['data'] = self.tron.toHex(text=str(options['message']))\n\n        sign = self.sign(tx)\n        result = self.broadcast(sign)\n\n        return result\n\n    def send_token(self, to, amount, token_id=None, account=None):\n        \"\"\"Transfer Token\n\n        Args:\n            to (str): is the recipient address\n            amount (float): is the amount of token to transfer\n            token_id (str): Token Name(NOT SYMBOL)\n            account: (str): is the address of the withdrawal account\n\n        Returns:\n            Token transfer Transaction raw data\n\n        \"\"\"\n        if account is None:\n            account = self.tron.default_address.hex\n\n        tx = self.tron.transaction_builder.send_token(\n            to,\n            amount,\n            token_id,\n            account\n        )\n        sign = self.sign(tx)\n        result = self.broadcast(sign)\n\n        return result\n\n    def freeze_balance(self, amount=0, duration=3, resource='BANDWIDTH', account=None):\n        \"\"\"\n        Freezes an amount of TRX.\n        Will give bandwidth OR Energy and TRON Power(voting rights)\n        to the owner of the frozen tokens.\n\n        Args:\n            amount (int): number of frozen trx\n            duration (int): duration in days to be frozen\n            resource (str): type of resource, must be either \"ENERGY\" or \"BANDWIDTH\"\n            account (str): address that is freezing trx account\n\n        \"\"\"\n\n        if account is None:\n            account = self.tron.default_address.hex\n\n        transaction = self.tron.transaction_builder.freeze_balance(\n            amount,\n            duration,\n            resource,\n            account\n        )\n        sign = self.sign(transaction)\n        response = self.broadcast(sign)\n\n        return response\n\n    def unfreeze_balance(self, resource='BANDWIDTH', account=None):\n        \"\"\"\n        Unfreeze TRX that has passed the minimum freeze duration.\n        Unfreezing will remove bandwidth and TRON Power.\n\n        Args:\n            resource (str): type of resource, must be either \"ENERGY\" or \"BANDWIDTH\"\n            account (str): address that is freezing trx account\n\n        \"\"\"\n\n        if account is None:\n            account = self.tron.default_address.hex\n\n        transaction = self.tron.transaction_builder.unfreeze_balance(\n            resource,\n            account\n        )\n        sign = self.sign(transaction)\n        response = self.broadcast(sign)\n\n        return response\n\n    def online_sign(self, transaction: dict):\n        \"\"\"Online transaction signature\n        Sign the transaction, the api has the risk of leaking the private key,\n        please make sure to call the api in a secure environment\n\n        Warnings:\n            Do not use this in any web / user-facing applications.\n            This will expose the private key.\n\n        Args:\n            transaction (dict): transaction details\n\n        \"\"\"\n\n        if 'signature' in transaction:\n            raise TronError('Transaction is already signed')\n\n        address = self.tron.address.from_private_key(self.tron.private_key).hex.lower()\n        owner_address = transaction['raw_data']['contract'][0]['parameter']['value']['owner_address']\n\n        if address != owner_address:\n            raise ValueError('Private key does not match address in transaction')\n\n        return self.tron.manager.request('/wallet/gettransactionsign', {\n            'transaction': transaction,\n            'privateKey': self.tron.private_key\n        })\n\n    def sign(self, transaction: Any, use_tron: bool = True, multisig: bool = False):\n        \"\"\"Safe method for signing your transaction\n\n        Warnings:\n            method: online_sign() - Use only in extreme cases.\n\n        Args:\n            transaction (Any): transaction details\n            use_tron (bool): is Tron header\n            multisig (bool): multi sign\n\n        \"\"\"\n\n        if is_string(transaction):\n            if not is_hex(transaction):\n                raise TronError('Expected hex message input')\n\n            # Determine which header to attach to the message\n            # before encrypting or decrypting\n            header = TRX_MESSAGE_HEADER if use_tron else ETH_MESSAGE_HEADER\n            header += str(len(transaction))\n\n            message_hash = self.tron.keccak(text=header+transaction)\n\n            signed_message = Account.sign_hash(self.tron.toHex(message_hash), self.tron.private_key)\n            return signed_message\n\n        if not multisig and 'signature' in transaction:\n            raise TronError('Transaction is already signed')\n\n        try:\n            if not multisig:\n                address = self.tron.address.from_private_key(self.tron.private_key).hex.lower()\n                owner_address = transaction['raw_data']['contract'][0]['parameter']['value']['owner_address']\n\n                if address != owner_address:\n                    raise ValueError('Private key does not match address in transaction')\n\n            # This option deals with signing of transactions, and writing to the array\n            signed_tx = Account.sign_hash(\n                transaction['txID'], self.tron.private_key\n            )\n            signature = signed_tx['signature'].hex()[2:]\n\n            # support multi sign\n            if 'signature' in transaction and is_list(transaction['signature']):\n                if not transaction['signature'].index(signature):\n                    transaction['signature'].append(signature)\n            else:\n                transaction['signature'] = [signature]\n\n            return transaction\n        except ValueError as err:\n            raise InvalidTronError(err)\n\n    def broadcast(self, signed_transaction):\n        \"\"\"Broadcast the signed transaction\n\n        Args:\n            signed_transaction (object): signed transaction contract data\n\n        \"\"\"\n        if not is_object(signed_transaction):\n            raise InvalidTronError('Invalid transaction provided')\n\n        if 'signature' not in signed_transaction:\n            raise TronError('Transaction is not signed')\n\n        response = self.tron.manager.request('/wallet/broadcasttransaction',\n                                             signed_transaction)\n\n        if 'result' in response:\n            response.update({\n                'transaction': signed_transaction\n            })\n        return response\n\n    def sign_and_broadcast(self, transaction: Any):\n        \"\"\"Sign and send to the network\n\n        Args:\n            transaction (Any): transaction details\n        \"\"\"\n        if not is_object(transaction):\n            raise TronError('Invalid transaction provided')\n\n        signed_tx = self.sign(transaction)\n        return self.broadcast(signed_tx)\n\n    def verify_message(self, message, signed_message=None, address=None, use_tron: bool = True):\n        \"\"\" Get the address of the account that signed the message with the given hash.\n        You must specify exactly one of: vrs or signature\n\n        Args:\n            message (str): The message in the format \"hex\"\n            signed_message (AttributeDict): Signature\n            address (str): is Address\n            use_tron (bool): is Tron header\n\n        \"\"\"\n        if address is None:\n            address = self.tron.default_address.base58\n\n        if not is_hex(message):\n            raise TronError('Expected hex message input')\n\n        # Determine which header to attach to the message\n        # before encrypting or decrypting\n        header = TRX_MESSAGE_HEADER if use_tron else ETH_MESSAGE_HEADER\n        header += str(len(message))\n\n        message_hash = self.tron.keccak(text=header+message)\n        recovered = Account.recover_hash(self.tron.toHex(message_hash), signed_message.signature)\n\n        tron_address = '41' + recovered[2:]\n        base58address = self.tron.address.from_hex(tron_address).decode()\n\n        if base58address == address:\n            return True\n\n        raise ValueError('Signature does not match')\n\n    def update_account(self, account_name, address=None):\n        \"\"\"Modify account name\n        Note: Username is allowed to edit only once.\n\n        Args:\n            account_name (str): name of the account\n            address (str): address\n\n        \"\"\"\n        if address is None:\n            address = self.tron.default_address.hex\n\n        transaction = self.tron.transaction_builder.update_account(\n            account_name,\n            address\n        )\n        sign = self.sign(transaction)\n        response = self.broadcast(sign)\n\n        return response\n\n    def apply_for_sr(self, url, address):\n        \"\"\"Apply to become a super representative\n        Note: Applied to become a super representative. Cost 9999 TRX.\n\n        Args:\n            url (str): official website address\n            address (str): address\n\n        \"\"\"\n\n        if address is None:\n            address = self.tron.default_address.hex\n\n        transaction = self.tron.transaction_builder.apply_for_sr(\n            url,\n            address\n        )\n        sign = self.sign(transaction)\n        response = self.broadcast(sign)\n\n        return response\n\n    def list_nodes(self):\n        \"\"\"List the nodes which the api fullnode is connecting on the network\"\"\"\n        response = self.tron.manager.request('/wallet/listnodes')\n        callback = map(lambda x: {\n            'address': '{}:{}'.format(self.tron.toText(x['address']['host']),\n                                      str(x['address']['port']))\n        }, response['nodes'])\n\n        return list(callback)\n\n    def get_tokens_issued_by_address(self, address):\n        \"\"\"List the tokens issued by an account.\n\n        Args:\n            address (str): address\n\n        Returns:\n            The token issued by the account.\n            An account can issue only one token.\n\n        \"\"\"\n\n        if not self.tron.isAddress(address):\n            raise InvalidTronError('Invalid address provided')\n\n        address = self.tron.address.to_hex(address)\n\n        return self.tron.manager.request('/wallet/getassetissuebyaccount', {\n            'address': address\n        })\n\n    def get_token_from_id(self, token_id: str):\n        \"\"\"Query token by name.\n\n        Args:\n            token_id (str): The name of the token\n\n        \"\"\"\n        if not isinstance(token_id, str) or not len(token_id):\n            raise InvalidTronError('Invalid token ID provided')\n\n        return self.tron.manager.request('/wallet/getassetissuebyname', {\n            'value': self.tron.toHex(text=token_id)\n        })\n\n    def get_block_range(self, start, end):\n        \"\"\"Query a range of blocks by block height\n\n        Args:\n            start (int): starting block height, including this block\n            end (int): ending block height, excluding that block\n\n        \"\"\"\n        if not is_integer(start) or start < 0:\n            raise InvalidTronError('Invalid start of range provided')\n\n        if not is_integer(end) or end <= start:\n            raise InvalidTronError('Invalid end of range provided')\n\n        response = self.tron.manager.request('/wallet/getblockbylimitnext', {\n            'startNum': int(start),\n            'endNum': int(end) + 1\n        }, 'post')\n\n        return response.get('block')\n\n    def get_latest_blocks(self, num=1):\n        \"\"\"Query the latest blocks\n\n        Args:\n            num (int): the number of blocks to query\n\n        \"\"\"\n        if not is_integer(num) or num <= 0:\n            raise InvalidTronError('Invalid limit provided')\n\n        response = self.tron.manager.request('/wallet/getblockbylatestnum', {\n            'num': num\n        })\n\n        return response.get('block')\n\n    def list_super_representatives(self):\n        \"\"\"Query the list of Super Representatives\"\"\"\n        response = self.tron.manager.request('/wallet/listwitnesses')\n        return response.get('witnesses')\n\n    def list_tokens(self, limit=0, offset=0):\n        \"\"\"Query the list of Tokens with pagination\n\n        Args:\n            limit (int): index of the starting Token\n            offset (int): number of Tokens expected to be returned\n\n        Returns:\n            List of Tokens\n\n        \"\"\"\n        if not is_integer(limit) or (limit and offset < 1):\n            raise InvalidTronError('Invalid limit provided')\n\n        if not is_integer(offset) or offset < 0:\n            raise InvalidTronError('Invalid offset provided')\n\n        if not limit:\n            return self.tron.manager.request('/wallet/getassetissuelist').get('assetIssue')\n\n        return self.tron.manager.request('/wallet/getpaginatedassetissuelist', {\n            'limit': int(limit),\n            'offset': int(offset)\n        })\n\n    def time_until_next_vote_cycle(self):\n        \"\"\"Get the time of the next Super Representative vote\n\n        Returns:\n            Number of milliseconds until the next voting time.\n\n        \"\"\"\n        num = self.tron.manager.request('/wallet/getnextmaintenancetime').get('num')\n\n        if num == -1:\n            raise Exception('Failed to get time until next vote cycle')\n\n        return math.floor(num / 1000)\n\n    def get_contract(self, contract_address):\n        \"\"\"Queries a contract's information from the blockchain.\n\n        Args:\n            contract_address (str): contract address\n\n        Returns:\n            SmartContract object.\n\n        \"\"\"\n\n        if not self.tron.isAddress(contract_address):\n            raise InvalidTronError('Invalid contract address provided')\n\n        return self.tron.manager.request('/wallet/getcontract', {\n            'value': self.tron.address.to_hex(contract_address)\n        })\n\n    def contract(self, address=None, **kwargs):\n        \"\"\"Work with a contract\n\n        Args:\n            address (str): TRON Address\n            **kwargs (any): details (bytecode, abi)\n        \"\"\"\n        factory_class = kwargs.pop('contract_factory_class', self.default_contract_factory)\n        contract_factory = factory_class.factory(self.tron, **kwargs)\n\n        if address:\n            return contract_factory(address)\n        return contract_factory\n\n    def validate_address(self, address, _is_hex=False):\n        \"\"\"Validate address\n\n        Args:\n            address (str): The address, should be in base58checksum\n            _is_hex (bool): hexString or base64 format\n\n        \"\"\"\n        if _is_hex:\n            address = self.tron.address.to_hex(address)\n\n        return self.tron.manager.request('/wallet/validateaddress', {\n            'address': address\n        })\n\n    def get_chain_parameters(self):\n        \"\"\"Getting chain parameters\"\"\"\n        return self.tron.manager.request('/wallet/getchainparameters')\n\n    def get_exchange_by_id(self, exchange_id):\n        \"\"\"Find exchange by id\n\n        Args:\n             exchange_id (str): ID Exchange\n\n        \"\"\"\n\n        if not isinstance(exchange_id, int) or exchange_id < 0:\n            raise InvalidTronError('Invalid exchangeID provided')\n\n        return self.tron.manager.request('/wallet/getexchangebyid', {\n            'id': exchange_id\n        })\n\n    def get_list_exchangers(self):\n        \"\"\"Get list exchangers\"\"\"\n        return self.tron.manager.request('/wallet/listexchanges')\n\n    def get_proposal(self, proposal_id):\n        \"\"\"Query proposal based on id\n\n        Args:\n            proposal_id (int): ID\n\n        \"\"\"\n        if not isinstance(proposal_id, int) or proposal_id < 0:\n            raise InvalidTronError('Invalid proposalID provided')\n\n        return self.tron.manager.request('/wallet/getproposalbyid', {\n            'id': int(proposal_id)\n        })\n\n    def list_proposals(self):\n        \"\"\"Query all proposals\n\n        Returns:\n            Proposal list information\n\n        \"\"\"\n        return self.tron.manager.request('/wallet/listproposals')\n\n    def vote_proposal(self, proposal_id, has_approval, voter_address):\n        \"\"\"Proposal approval\n\n        Args:\n            proposal_id (int): proposal id\n            has_approval (bool): Approved\n            voter_address (str): Approve address\n\n        Returns:\n             Approval of the proposed transaction\n\n        \"\"\"\n\n        if voter_address is None:\n            voter_address = self.tron.default_address.hex\n\n        transaction = self.tron.transaction_builder.vote_proposal(\n            proposal_id,\n            has_approval,\n            voter_address\n        )\n        sign = self.sign(transaction)\n        response = self.broadcast(sign)\n\n        return response\n\n    def proposal_delete(self, proposal_id: int, issuer_address: str):\n        \"\"\"Delete proposal\n\n        Args:\n            proposal_id (int): proposal id\n            issuer_address (str): delete the person's address\n\n        Results:\n            Delete the proposal's transaction\n\n        \"\"\"\n        if issuer_address is None:\n            issuer_address = self.tron.default_address.hex\n\n        transaction = self.tron.transaction_builder.delete_proposal(\n            proposal_id,\n            issuer_address\n        )\n        sign = self.sign(transaction)\n        response = self.broadcast(sign)\n\n        return response\n\n    def list_exchanges_paginated(self, limit=10, offset=0):\n        \"\"\"Paged query transaction pair list\n\n        Args:\n            limit (int): number of trading pairs  expected to be returned.\n            offset (int): index of the starting trading pair\n\n        \"\"\"\n        return self.tron.manager.request('/wallet/listexchangespaginated', {\n            'limit': limit,\n            'offset': offset\n        })\n\n    def get_node_info(self):\n        \"\"\"Get info about thre node\"\"\"\n        return self.tron.manager.request('/wallet/getnodeinfo')\n\n    def get_token_list_name(self, token_id: str) -> any:\n        \"\"\"Query token list by name.\n\n            Args:\n                token_id (str): The name of the token\n        \"\"\"\n        if not is_string(token_id):\n            raise ValueError('Invalid token ID provided')\n\n        return self.tron.manager.request('/wallet/getassetissuelistbyname', {\n            'value': self.tron.toHex(text=token_id)\n        })\n\n    def get_token_by_id(self, token_id: str) -> any:\n        \"\"\"Query token by id.\n\n            Args:\n                token_id (str): The id of the token, it's a string\n        \"\"\"\n        if not is_string(token_id):\n            raise ValueError('Invalid token ID provided')\n\n        return self.tron.manager.request('/wallet/getassetissuebyid', {\n            'value': token_id\n        })\n"
  }
]