Full Code of iexbase/tron-api-python for AI

master 2da3b705d0bb cached
50 files
190.1 KB
44.1k tokens
335 symbols
1 requests
Download .txt
Showing preview only (203K chars total). Download the full file or copy to clipboard to get everything.
Repository: iexbase/tron-api-python
Branch: master
Commit: 2da3b705d0bb
Files: 50
Total size: 190.1 KB

Directory structure:
gitextract_aju8mwnx/

├── .bumpversion.cfg
├── .coveragerc
├── .gitignore
├── .pyup.yml
├── .travis.yml
├── LICENSE
├── README.rst
├── codecov.yml
├── docs/
│   ├── index.rst
│   ├── overview.rst
│   ├── quickstart.rst
│   ├── releases.rst
│   └── v2_migration.rst
├── example.py
├── examples/
│   ├── account.py
│   ├── address-hex.py
│   ├── amount.py
│   ├── contract.py
│   ├── custom-nodes.py
│   ├── find-transaction.py
│   ├── send-transaction.py
│   └── sign.py
├── requirements-docs.txt
├── setup.py
└── tronapi/
    ├── __init__.py
    ├── common/
    │   ├── __init__.py
    │   ├── abi.py
    │   ├── account.py
    │   ├── blocks.py
    │   ├── contracts.py
    │   ├── datastructures.py
    │   ├── datatypes.py
    │   ├── encoding.py
    │   ├── formatters.py
    │   ├── normalizers.py
    │   ├── threads.py
    │   ├── toolz/
    │   │   └── __init__.py
    │   ├── transactions.py
    │   └── validation.py
    ├── constants.py
    ├── contract.py
    ├── exceptions.py
    ├── main.py
    ├── manager.py
    ├── module.py
    ├── providers/
    │   ├── __init__.py
    │   ├── base.py
    │   └── http.py
    ├── transactionbuilder.py
    └── trx.py

================================================
FILE CONTENTS
================================================

================================================
FILE: .bumpversion.cfg
================================================
[bumpversion]
current_version = 3.1.5
commit = True
tag = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(-(?P<stage>[^.]*)\.(?P<devnum>\d+))?
serialize =
	{major}.{minor}.{patch}-{stage}.{devnum}
	{major}.{minor}.{patch}

[bumpversion:part:stage]
optional_value = stable
first_value = stable
values =
	alpha
	beta
	stable

[bumpversion:part:devnum]

[bumpversion:file:setup.py]
search = version='{current_version}',
replace = version='{new_version}',


================================================
FILE: .coveragerc
================================================
[run]
branch = True
include = */tronapi/*

================================================
FILE: .gitignore
================================================
.idea

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# pyenv
.python-version

# celery beat schedule file
celerybeat-schedule

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/


================================================
FILE: .pyup.yml
================================================
# autogenerated pyup.io config file
# see https://pyup.io/docs/configuration/ for all available options

update: all

# update schedule
# default: empty
# allowed: "every day", "every week", ..
schedule: "every week"

pin: False

================================================
FILE: .travis.yml
================================================
sudo: required
language: python
os: linux
cache: pip
dist: trusty
python:
  - "3.6"
  - "3.7-dev"
  - "nightly" # currently points to 3.7-dev
# command to install dependencies
install: "pip install -U setuptools setuptools_scm codecov pandas"
# command to run tests
script:
  - python setup.py test
  - coverage run setup.py test
  
after_success:
  - codecov


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2018 iEXBase

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.rst
================================================
===================
TRON API for Python
===================

A Python API for interacting with the Tron (TRX)

.. image:: https://img.shields.io/pypi/v/tronapi.svg
    :target: https://pypi.python.org/pypi/tronapi

.. image:: https://img.shields.io/pypi/pyversions/tronapi.svg
    :target: https://pypi.python.org/pypi/tronapi

.. image:: https://api.travis-ci.com/iexbase/tron-api-python.svg?branch=master
    :target: https://travis-ci.com/iexbase/tron-api-python
    
.. image:: https://img.shields.io/github/issues/iexbase/tron-api-python.svg
    :target: https://github.com/iexbase/tron-api-python/issues
    
.. image:: https://img.shields.io/github/issues-pr/iexbase/tron-api-python.svg
    :target: https://github.com/iexbase/tron-api-python/pulls

.. image:: https://api.codacy.com/project/badge/Grade/8a5ae1e1cc834869b1094ea3b0d24f78
   :alt: Codacy Badge
   :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
    

------------

**A Command-Line Interface framework**

You can install it in a system-wide location via pip:

.. code-block:: bash

    sudo pip3 install tronapi

Or install it locally using `virtualenv <https://github.com/pypa/virtualenv>`__:

.. code-block:: bash

    virtualenv -p /usr/bin/python3 ~/tronapi
    source ~/tronapi/bin/activate
    pip3 install tronapi

------------

Usage
=====
Specify the API endpoints:


Smart Contract
--------------

.. code-block:: python

    from tronapi import Tron
    from solc import compile_source

    full_node = 'https://api.trongrid.io'
    solidity_node = 'https://api.trongrid.io'
    event_server = 'https://api.trongrid.io'

    tron = Tron(full_node=full_node,
            solidity_node=solidity_node,
            event_server=event_server)

    # or default (tron = Tron())


    # Solidity source code
    contract_source_code = '''
    pragma solidity ^0.4.25;

    contract Hello {
        string public message;

        function Hello(string initialMessage) public {
            message = initialMessage;
        }

        function setMessage(string newMessage) public {
            message = newMessage;
        }
    }

    '''

    compiled_sol = compile_source(contract_source_code)
    contract_interface = compiled_sol['<stdin>:Hello']

    hello = tron.trx.contract(
        abi=contract_interface['abi'],
        bytecode=contract_interface['bin']
    )

    # Submit the transaction that deploys the contract
    tx = hello.deploy(
        fee_limit=10**9,
        call_value=0,
        consume_user_resource_percent=1
    )

..

Base Example
------------

.. code-block:: python
    
    from tronapi import Tron
    logging.basicConfig(level=logging.DEBUG, format="%(asctime)s - %(levelname)s - %(message)s")
    logger = logging.getLogger()

    full_node = 'https://api.trongrid.io'
    solidity_node = 'https://api.trongrid.io'
    event_server = 'https://api.trongrid.io'

    tron = Tron(full_node=full_node,
            solidity_node=solidity_node,
            event_server=event_server)

    account = tron.create_account
    is_valid = bool(tron.trx.is_address(account.address.hex))

    logger.debug('Generated account: ')
    logger.debug('- Private Key: ' + account.private_key)
    logger.debug('- Public Key: ' + account.public_key)
    logger.debug('- Address: ')
    logger.debug('-- Base58: ' + account.address.base58)
    logger.debug('-- Hex: ' + account.address.hex)
    logger.debug('-- isValid: ' + str(is_valid))
    logger.debug('-----------')
    
    transaction = tron.trx.get_transaction('757a14cef293c69b1cf9b9d3d19c2e40a330c640b05c6ffa4d54609a9628758c')

    logger.debug('Transaction: ')
    logger.debug('- Hash: ' + transaction['txID'])
    logger.debug('- Transaction: ' + json.dumps(transaction, indent=2))
    logger.debug('-----------')
    
    # Events
    event_result = tron.trx.get_event_result('TGEJj8eus46QMHPgWQe1FJ2ymBXRm96fn1', 0, 'Notify')

    logger.debug('Event result:')
    logger.debug('Contract Address: TGEJj8eus46QMHPgWQe1FJ2ymBXRm96fn1')
    logger.debug('Event Name: Notify')
    logger.debug('Block Number: 32162')
    logger.debug('- Events: ' + json.dumps(event_result, indent=2))

More samples and snippets are available at `examples <https://github.com/iexbase/tron-api-python/tree/master/examples>`__.

Documentation
=============

Documentation is available at `docs <https://tronapi-for-python.readthedocs.io/en/latest/>`__.


Donations
=============

TRON: TRWBqiqoFZysoAeyR1J35ibuyc8EvhUAoY



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

codecov:
  branch: master     # the branch to show by default

coverage:
  precision: 2
  round: down
  range: "70...100"

  status:
    project:
      default:
        target: auto
        if_no_uploads: error

    patch:
      default:
        target: "80%"
        if_no_uploads: error

  ignore:          # files and folders that will be removed during processing
    - "docs/*"
    - "examples/*"

comment:
  # @stevepeak (from codecov.io) suggested we change 'suggestions' to 'uncovered'
  # in the following line. Thanks Steve!
  layout: "header, diff, changes, sunburst, uncovered"
  behavior: default


================================================
FILE: docs/index.rst
================================================
TronAPI
=======

TronAPI is a python library for interacting with Tron Protocol.

Contents
--------

.. toctree::
    :maxdepth: 1

    quickstart
    overview
    node
    v2_migration
    releases

================================================
FILE: docs/overview.rst
================================================
Overview
========

.. contents:: :local:

The common entrypoint for interacting with the Tron library is the ``Tron``
object.  The tron object provides APIs for interacting with the tron
blockchain, typically by connecting to a HTTP server.

Providers
---------

*Providers* are how tron connects to the blockchain.  The TronAPI library comes
with a the following built-in providers that should be suitable for most normal
use cases.

- ``HttpProvider`` for connecting to http and https based servers.

The ``HttpProvider`` takes the full URI where the server can be found.  For
local development this would be something like ``http://localhost:8090``.


.. code-block:: python

    >>> from tronapi import HttpProvider, Tron

    # Note that you should create only one HttpProvider per
    # process, as it recycles underlying TCP/IP network connections between
    # your process and Tron node

    >>> full_node = HttpProvider('http://localhost:8090')
    >>> solidity_node = HttpProvider('http://localhost:8090')
    >>> event_server = HttpProvider('http://localhost:8090')

    >>> tron = Tron(full_node, solidity_node, event_server)


Base API
--------

The ``Tron`` class exposes the following convenience APIs.

.. _overview_type_conversions:

Type Conversions
~~~~~~~~~~~~~~~~

.. py:method:: Tron.toHex(primitive=None, hexstr=None, text=None)

    Takes a variety of inputs and returns it in its hexadecimal representation.

    .. code-block:: python

        >>> Tron.toHex(0)
        '0x0'
        >>> Tron.toHex(1)
        '0x1'
        >>> Tron.toHex(0x0)
        '0x0'
        >>> Tron.toHex(0x000F)
        '0xf'
        >>> Tron.toHex(b'')
        '0x'
        >>> Tron.toHex(b'\x00\x0F')
        '0x000f'
        >>> Tron.toHex(False)
        '0x0'
        >>> Tron.toHex(True)
        '0x1'
        >>> Tron.toHex(hexstr='0x000F')
        '0x000f'
        >>> Tron.toHex(hexstr='000F')
        '0x000f'
        >>> Tron.toHex(text='')
        '0x'
        >>> Tron.toHex(text='cowmö')
        '0x636f776dc3b6'

.. py:method:: Tron.toText(primitive=None, hexstr=None, text=None)

    Takes a variety of inputs and returns its string equivalent.
    Text gets decoded as UTF-8.


    .. code-block:: python

        >>> Tron.toText(0x636f776dc3b6)
        'cowmö'
        >>> Tron.toText(b'cowm\xc3\xb6')
        'cowmö'
        >>> Tron.toText(hexstr='0x636f776dc3b6')
        'cowmö'
        >>> Tron.toText(hexstr='636f776dc3b6')
        'cowmö'
        >>> Tron.toText(text='cowmö')
        'cowmö'


.. py:method:: Tron.toBytes(primitive=None, hexstr=None, text=None)

    Takes a variety of inputs and returns its bytes equivalent.
    Text gets encoded as UTF-8.


    .. code-block:: python

        >>> Tron.toBytes(0)
        b'\x00'
        >>> Tron.toBytes(0x000F)
        b'\x0f'
        >>> Tron.toBytes(b'')
        b''
        >>> Tron.toBytes(b'\x00\x0F')
        b'\x00\x0f'
        >>> Tron.toBytes(False)
        b'\x00'
        >>> Tron.toBytes(True)
        b'\x01'
        >>> Tron.toBytes(hexstr='0x000F')
        b'\x00\x0f'
        >>> Tron.toBytes(hexstr='000F')
        b'\x00\x0f'
        >>> Tron.toBytes(text='')
        b''
        >>> Tron.toBytes(text='cowmö')
        b'cowm\xc3\xb6'


.. py:method:: Tron.toInt(primitive=None, hexstr=None, text=None)

    Takes a variety of inputs and returns its integer equivalent.


    .. code-block:: python

        >>> Tron.toInt(0)
        0
        >>> Tron.toInt(0x000F)
        15
        >>> Tron.toInt(b'\x00\x0F')
        15
        >>> Tron.toInt(False)
        0
        >>> Tron.toInt(True)
        1
        >>> Tron.toInt(hexstr='0x000F')
        15
        >>> Tron.toInt(hexstr='000F')
        15

.. _overview_currency_conversions:

Currency Conversions
~~~~~~~~~~~~~~~~~~~~~

.. py:method:: Tron.toSun(value)

    Returns the value in the denomination specified by the ``currency`` argument
    converted to sun.


    .. code-block:: python

        >>> tron.toSun(1)
        1000000


.. py:method:: Tron.fromSun(value)

    Returns the value in wei converted to the given currency. The value is returned
    as a ``Decimal`` to ensure precision down to the wei.


    .. code-block:: python

        >>> tron.fromSun(1000000)
        Decimal('1')


.. _overview_addresses:

Addresses
~~~~~~~~~~~~~~~~

.. py:method:: Tron.isAddress(value)

    Returns ``True`` if the value is one of the recognized address formats.

    .. code-block:: python

        >>> tron.isAddress('TRWBqiqoFZysoAeyR1J35ibuyc8EvhUAoY')
        True


.. _overview_hashing:


Cryptographic Hashing
~~~~~~~~~~~~~~~~~~~~~

.. py:classmethod:: Tron.sha3(primitive=None, hexstr=None, text=None)

    Returns the Keccak SHA256 of the given value. Text is encoded to UTF-8 before
    computing the hash, just like Solidity. Any of the following are
    valid and equivalent:

    .. code-block:: python

        >>> Tron.sha3(0x747874)
        >>> Tron.sha3(b'\x74\x78\x74')
        >>> Tron.sha3(hexstr='0x747874')
        >>> Tron.sha3(hexstr='747874')
        >>> Tron.sha3(text='txt')
        HexBytes('0xd7278090a36507640ea6b7a0034b69b0d240766fa3f98e3722be93c613b29d2e')



================================================
FILE: docs/quickstart.rst
================================================
Quickstart
==========

.. contents:: :local:

.. NOTE:: All code starting with a ``$`` is meant to run on your terminal.
    All code starting with a ``>>>`` is meant to run in a python interpreter,
    like `ipython <https://pypi.org/project/ipython/>`_.

Installation
------------

TronAPI can be installed (preferably in a :ref:`virtualenv <setup_environment>`)
using ``pip`` as follows:

.. code-block:: shell

   $ pip install tronapi


.. NOTE:: If you run into problems during installation, you might have a
    broken environment. See the troubleshooting guide to :ref:`setup_environment`.


Installation from source can be done from the root of the project with the
following command.

.. code-block:: shell

   $ pip install .


Using TronAPI
----------

To use the tron library you will need to initialize the
:class:`~tronapi` class.

.. code-block:: python

    >>> from tronapi import Tron
    >>> full_node = HttpProvider('https://api.trongrid.io')
    >>> solidity_node = HttpProvider('https://api.trongrid.io')
    >>> event_server = 'https://api.trongrid.io'
    >>>
    >>> tron = Tron(full_node, solidity_node, event_server)
    >>> tron.default_block = 'latest'


Getting Blockchain Info
----------------------------------------

It's time to start using TronAPI for Python! Try getting all the information about the latest block.

.. code-block:: python

    >>> tron.get_block('latest')
    >>> {
    "blockID": "00000000003a5bbda4aea15cb5d99230674463e9d5f2c0c647316839b25fd5b9",
    "block_header": {
        "raw_data": {
            "number": 3824573,
            "txTrieRoot": "31ee3e2ed28f843bf1d53495beece2f5b9c76480772f0106e17156fb0066c3a2",
            "witness_address": "41f70386347e689e6308e4172ed7319c49c0f66e0b",
            "parentHash": "00000000003a5bbc1e78e3144ad52f01a27b8f7acceb98d3ca09c1abea5cd32a",
            "version": 3,
            "timestamp": 1541425827000
        },
        "witness_signature": "fddc729f55c0ecc6f9cf4ab17cf818ddc0e85d2c21382ed6b1430adb1dcd13006c24ae0e08f16d29362452ec8869d29a28d57a85d6cec30ef60c2a37332fdb4d00"
    },
    "transactions": [

    ]
}





================================================
FILE: docs/releases.rst
================================================


================================================
FILE: docs/v2_migration.rst
================================================
Migrating your code from v1 to v2
=======================================

Changes to base API convenience methods
---------------------------------------

Tron.toDecimal()
~~~~~~~~~~~~~~~~~

In v4 ``Tron.toDecimal()`` is renamed: :meth:`~Tron.toInt` for improved clarity. It does not return a :class:`decimal.Decimal`, it returns an :class:`int`.


Removed Methods
~~~~~~~~~~~~~~~~~~

- ``Tron.toUtf8`` was removed for :meth:`~Tron.toText`.
- ``Tron.fromUtf8`` was removed for :meth:`~Tron.toHex`.
- ``Tron.toAscii`` was removed for :meth:`~Tron.toBytes`.
- ``Tron.fromAscii`` was removed for :meth:`~Tron.toHex`.
- ``Tron.fromDecimal`` was removed for :meth:`~Tron.toHex`.

Provider Access
~~~~~~~~~~~~~~~~~

In v2, ``tron.currentProvider`` was removed, in favor of ``tron.providers``.

Disambiguating String Inputs
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

There are a number of places where an arbitrary string input might be either
a byte-string that has been hex-encoded, or unicode characters in text.
These are named ``hexstr`` and ``text`` in TronAPI.
You specify which kind of :class:`str` you have by using the appropriate
keyword argument. See examples in :ref:`overview_type_conversions`.

In v1, some methods accepted a :class:`str` as the first positional argument.
In v2, you must pass strings as one of ``hexstr`` or ``text`` keyword arguments.

Notable methods that no longer accept ambiguous strings:

- :meth:`~Tron.sha3`
- :meth:`~Tron.toBytes`

================================================
FILE: example.py
================================================
import json
import logging

from tronapi import Tron

logging.basicConfig(level=logging.DEBUG, format="%(asctime)s - %(levelname)s - %(message)s")
logger = logging.getLogger()

full_node = 'https://api.trongrid.io'
solidity_node = 'https://api.trongrid.io'
event_server = 'https://api.trongrid.io/'
private_key = 'da146374a75310b9666e834ee4ad0866d6f4035967bfc76217c5a495fff9f0d0'

tron = Tron(full_node=full_node,
            solidity_node=solidity_node,
            event_server=event_server)


account = tron.create_account
is_valid = bool(tron.isAddress(account.address.hex))


logger.debug('Generated account: ')
logger.debug('- Private Key: ' + account.private_key)
logger.debug('- Public Key: ' + account.public_key)
logger.debug('- Address: ')
logger.debug('-- Base58: ' + account.address.base58)
logger.debug('-- Hex: ' + account.address.hex)
logger.debug('-- isValid: ' + str(is_valid))
logger.debug('-----------')

current_block = tron.trx.get_current_block()
logger.debug('Current block: ')
logger.debug(json.dumps(current_block, indent=2))
logger.debug('-----------')

previous_block = tron.trx.get_block(0)

logger.debug('Previous block #52: ')
logger.debug(json.dumps(previous_block, indent=2))
logger.debug('-----------')


genesis_block_count = tron.trx.get_block_transaction_count('earliest')
logger.debug('Genesis Block Transaction Count: ')
logger.debug('Transactions:' + str(genesis_block_count))
logger.debug('-----------')

transaction = tron.trx.get_transaction('757a14cef293c69b1cf9b9d3d19c2e40a330c640b05c6ffa4d54609a9628758c')

logger.debug('Transaction: ')
logger.debug('- Hash: ' + transaction['txID'])
logger.debug('- Transaction: ' + json.dumps(transaction, indent=2))
logger.debug('-----------')


account_info = tron.trx.get_account('TKLnCNY5EsLNCvCXQTCn1dtqvc6vHhJUyJ')

logger.debug('Account information: ')
logger.debug('- Address: TKLnCNY5EsLNCvCXQTCn1dtqvc6vHhJUyJ')
logger.debug('- Account:' + json.dumps(account_info, indent=2))
logger.debug('-----------')


balance = tron.trx.get_account('TKLnCNY5EsLNCvCXQTCn1dtqvc6vHhJUyJ')

logger.debug('Account balance: ')
logger.debug('- Address: TKLnCNY5EsLNCvCXQTCn1dtqvc6vHhJUyJ')
logger.debug('- Account:' + json.dumps(balance, indent=2))
logger.debug('-----------')


band_width = tron.trx.get_band_width('TKLnCNY5EsLNCvCXQTCn1dtqvc6vHhJUyJ')

logger.debug('Account bandwidth: ')
logger.debug('- Address: TKLnCNY5EsLNCvCXQTCn1dtqvc6vHhJUyJ')
logger.debug('- Bandwidth:' + json.dumps(band_width, indent=2))
logger.debug('-----------')


list_nodes = tron.trx.list_nodes()

logger.debug('List of full nodes: ')
logger.debug('- Node Count:' + str(len(list_nodes)))
logger.debug('- Nodes:' + json.dumps(list_nodes, indent=2))
logger.debug('-----------')


block_ids = tron.trx.get_block_range(30, 35)
block = list(map(lambda x: {'id': x['block_header']['raw_data']['number'] or 0}, block_ids))

logger.debug('Block IDs between 30 and 35: ')
logger.debug('- Block Range: [ 30, 35 ]')
logger.debug('- Blocks IDs:' + json.dumps(block, indent=2))
logger.debug('-----------')


# send = tron.send_trx('TGEJj8eus46QMHPgWQe1FJ2ymBXRm96fn1', 10)
# logger.debug('Send TRX transaction: ')
# logger.debug('- Result: ' + json.dumps(send, indent=2))
# logger.debug('-----------')


event_result = tron.get_event_result('TGEJj8eus46QMHPgWQe1FJ2ymBXRm96fn1', 0, 'Notify')

logger.debug('Event result:')
logger.debug('Contract Address: TGEJj8eus46QMHPgWQe1FJ2ymBXRm96fn1')
logger.debug('Event Name: Notify')
logger.debug('Block Number: 32162')
logger.debug('- Events: ' + json.dumps(event_result, indent=2))


event_by_transaction_id = tron.get_event_transaction_id('32d7efe5f70c044bcd831f21f911209a7abf4ed0d5934b2c1b804e108008cd43')

logger.debug('Specific event result:')
logger.debug('Transaction: 32d7efe5f70c044bcd831f21f911209a7abf4ed0d5934b2c1b804e108008cd43')
logger.debug('- Events: ' + json.dumps(event_by_transaction_id, indent=2))


first_transaction = tron.trx.get_transaction_from_block(0, 0)

logger.debug('First transaction from block 0')
logger.debug('- Transaction: ' + json.dumps(first_transaction, indent=2))



================================================
FILE: examples/account.py
================================================

import logging
from tronapi import Tron

logging.basicConfig(level=logging.DEBUG, format="%(asctime)s - %(levelname)s - %(message)s")
logger = logging.getLogger()

full_node = 'https://api.trongrid.io'
solidity_node = 'https://api.trongrid.io'
event_server = 'https://api.trongrid.io/'
private_key = 'da146374a75310b9666e834ee4ad0866d6f4035967bfc76217c5a495fff9f0d0'

tron = Tron(full_node=full_node,
            solidity_node=solidity_node,
            event_server=event_server)


account = tron.create_account
is_valid = bool(tron.isAddress(account.address.hex))


logger.debug('Generated account: ')
logger.debug('- Private Key: ' + account.private_key)
logger.debug('- Public Key: ' + account.public_key)
logger.debug('- Address: ')
logger.debug('-- Base58: ' + account.address.base58)
logger.debug('-- Hex: ' + account.address.hex)
logger.debug('-- isValid: ' + str(is_valid))
logger.debug('-----------')



================================================
FILE: examples/address-hex.py
================================================
from tronapi import Tron
from tronapi import HttpProvider

full_node = HttpProvider('https://api.trongrid.io')
solidity_node = HttpProvider('https://api.trongrid.io')
event_server = HttpProvider('https://api.trongrid.io')
tron = Tron(full_node=full_node,
            solidity_node=solidity_node,
            event_server=event_server)


tron.address.to_hex('TT67rPNwgmpeimvHUMVzFfKsjL9GZ1wGw8')
# result: 41BBC8C05F1B09839E72DB044A6AA57E2A5D414A10

tron.address.from_hex('41BBC8C05F1B09839E72DB044A6AA57E2A5D414A10')
# result: TT67rPNwgmpeimvHUMVzFfKsjL9GZ1wGw8


================================================
FILE: examples/amount.py
================================================
from tronapi import Tron
from tronapi import HttpProvider

full_node = HttpProvider('https://api.trongrid.io')
solidity_node = HttpProvider('https://api.trongrid.io')
event_server = HttpProvider('https://api.trongrid.io')
tron = Tron(full_node=full_node,
            solidity_node=solidity_node,
            event_server=event_server)



tron.toSun(1)
# result: 1000000

tron.fromSun(1000000)
# result: 1




================================================
FILE: examples/contract.py
================================================
from tronapi import Tron, HttpProvider
from solc import compile_source

full_node = HttpProvider('https://api.trongrid.io')
solidity_node = HttpProvider('https://api.trongrid.io')
event_server = HttpProvider('https://api.trongrid.io')

tron = Tron(full_node=full_node,
            solidity_node=solidity_node,
            event_server=event_server)


# Solidity source code
contract_source_code = '''
pragma solidity ^0.4.25;

contract Hello {
    string public message;

    function Hello(string initialMessage) public {
        message = initialMessage;
    }

    function setMessage(string newMessage) public {
        message = newMessage;
    }
}

'''

compiled_sol = compile_source(contract_source_code)
contract_interface = compiled_sol['<stdin>:Hello']

hello = tron.trx.contract(
    abi=contract_interface['abi'],
    bytecode=contract_interface['bin']
)

# Submit the transaction that deploys the contract
tx_data = hello.deploy(
    fee_limit=10**9,
    call_value=0,
    consume_user_resource_percent=1
)

sign = tron.trx.sign(tx_data)
result = tron.trx.broadcast(sign)


================================================
FILE: examples/custom-nodes.py
================================================
from tronapi import Tron
from tronapi import HttpProvider

full_node = HttpProvider('https://api.trongrid.io')
solidity_node = HttpProvider('https://api.trongrid.io')
event_server = HttpProvider('https://api.trongrid.io')

# option 1
tron = Tron(full_node=full_node,
            solidity_node=solidity_node,
            event_server=event_server)

# option 2
tron_v2 = Tron()

# option 3
tron_v3 = Tron(
    default_address='TRWBqiqoFZysoAeyR1J35ibuyc8EvhUAoY',
    private_key='...'
)


================================================
FILE: examples/find-transaction.py
================================================
from tronapi import Tron
from tronapi import HttpProvider

full_node = HttpProvider('https://api.trongrid.io')
solidity_node = HttpProvider('https://api.trongrid.io')
event_server = HttpProvider('https://api.trongrid.io')
tron = Tron(full_node=full_node,
            solidity_node=solidity_node,
            event_server=event_server)



result = tron.trx.get_transaction('TxId')


================================================
FILE: examples/send-transaction.py
================================================
from tronapi import Tron
from tronapi import HttpProvider

full_node = HttpProvider('https://api.trongrid.io')
solidity_node = HttpProvider('https://api.trongrid.io')
event_server = HttpProvider('https://api.trongrid.io')

tron = Tron(full_node=full_node,
            solidity_node=solidity_node,
            event_server=event_server)


tron.private_key = 'private_key'
tron.default_address = 'default address'

# added message
send = tron.trx.send_transaction('to', 1)

print(send)


================================================
FILE: examples/sign.py
================================================
from tronapi import Tron
from tronapi import HttpProvider

full_node = HttpProvider('https://api.trongrid.io')
solidity_node = HttpProvider('https://api.trongrid.io')
event_server = HttpProvider('https://api.trongrid.io')

tron = Tron(full_node=full_node,
            solidity_node=solidity_node,
            event_server=event_server)


tron.private_key = 'private_key'
tron.default_address = 'default address'

# create transaction
create_tx = tron.transaction_builder.send_transaction('to', 1, 'from')

# offline sign
offline_sign = tron.trx.sign(create_tx)


# online sign (Not recommended)
online_sign = tron.trx.online_sign(create_tx)



================================================
FILE: requirements-docs.txt
================================================
.[docs]


================================================
FILE: setup.py
================================================
#!/usr/bin/env python
# --------------------------------------------------------------------
# Copyright (c) iEXBase. All rights reserved.
# Licensed under the MIT License.
# See License.txt in the project root for license information.
# --------------------------------------------------------------------

"""
    setup
    =====

    Tron: A Python API for interacting with Tron (TRX)

    :copyright: © 2018 by the iEXBase.
    :license: MIT License
"""

import os
import platform
from setuptools import (
    find_packages,
    setup,
)

py_version = platform.python_version()

PACKAGE_VERSION = '3.1.5'

EXTRAS_REQUIRE = {
    'tester': [
        'coverage',
        'pep8',
        'pyflakes',
        'pylint',
        'pytest-cov'
    ],

    'docs': [
        "mock",
        "sphinx-better-theme>=0.1.4",
        "click>=5.1",
        "configparser==3.5.0",
        "contextlib2>=0.5.4",
        "py-solc>=0.4.0",
        "pytest>=2.7.2",
        "sphinx",
        "sphinx_rtd_theme>=0.1.9",
        "toposort>=1.4",
        "urllib3",
        "tronapi",
        "wheel >= 0.31.0"
    ],

    'dev': [
        "bumpversion",
        "flaky>=3.3.0",
        "hypothesis>=3.31.2",
        "pytest>=3.5.0,<4",
        "pytest-mock==1.*",
        "pytest-pythonpath>=0.3",
        "pytest-watch==4.*",
        "pytest-xdist==1.*",
        "setuptools>=38.6.0",
        "tox>=1.8.0",
        "twine >= 1.11.0",
        "tqdm",
        "when-changed"
    ]

}

EXTRAS_REQUIRE['dev'] = (
        EXTRAS_REQUIRE['tester'] +
        EXTRAS_REQUIRE['docs'] +
        EXTRAS_REQUIRE['dev']
)

install_requires = [
    "toolz>=0.9.0,<1.0.0;implementation_name=='pypy'",
    "cytoolz>=0.9.0,<1.0.0;implementation_name=='cpython'",

    "eth-abi>=2.0.0b6,<3.0.0",
    "eth-account==0.4.0",
    "eth-utils>=1.3.0,<2.0.0",
    "eth-hash[pycryptodome]>=0.2.0,<1.0.0",

    "trx-utils",

    "hexbytes>=0.1.0,<1.0.0",

    "requests>=2.16.0,<3.0.0",
    "base58",
    "ecdsa",
    'attrdict',
]

this_dir = os.path.dirname(__file__)
readme_filename = os.path.join(this_dir, 'README.rst')

with open(readme_filename) as f:
    PACKAGE_LONG_DESCRIPTION = f.read()


setup(
    name='tronapi',
    version=PACKAGE_VERSION,
    description='A Python API for interacting with Tron (TRX)',
    long_description=PACKAGE_LONG_DESCRIPTION,
    long_description_content_type='text/x-rst',
    keywords='tron tron-api tron-api-python iexbase',
    url='https://github.com/iexbase/tron-api-python',
    author='Shamsudin Serderov',
    author_email='steein.shamsudin@gmail.com',
    license='MIT License',
    zip_safe=False,
    python_requires='>=3.6,<4',
    classifiers=[
        'Development Status :: 3 - Alpha',
        'Intended Audience :: Developers',
        'License :: OSI Approved :: MIT License',
        'Natural Language :: English',
        'Programming Language :: Python :: 3',
        'Programming Language :: Python :: 3.6',
        'Programming Language :: Python :: 3.7',
    ],
    packages=find_packages(exclude=['examples']),
    include_package_data=True,
    install_requires=install_requires,
    tests_require=EXTRAS_REQUIRE['tester'],
    extras_require=EXTRAS_REQUIRE,
)


================================================
FILE: tronapi/__init__.py
================================================
# --------------------------------------------------------------------
# Copyright (c) iEXBase. All rights reserved.
# Licensed under the MIT License.
# See License.txt in the project root for license information.
# --------------------------------------------------------------------

import sys

import pkg_resources

from eth_account import Account  # noqa: E402
from tronapi.providers.http import HttpProvider  # noqa: E402
from tronapi.main import Tron  # noqa: E402

if sys.version_info < (3, 5):
    raise EnvironmentError("Python 3.5 or above is required")


__version__ = pkg_resources.get_distribution("tronapi").version

__all__ = [
    '__version__',
    'HttpProvider',
    'Account',
    'Tron',
]


================================================
FILE: tronapi/common/__init__.py
================================================


================================================
FILE: tronapi/common/abi.py
================================================
# --------------------------------------------------------------------
# Copyright (c) iEXBase. All rights reserved.
# Licensed under the MIT License.
# See License.txt in the project root for license information.
# --------------------------------------------------------------------

import binascii
import itertools
import re

from collections import (
    namedtuple,
)

from eth_abi import (
    encoding,
    decoding
)

from eth_abi.codec import ABICodec
from eth_abi.registry import (
    BaseEquals,
    registry as default_registry,
)

from eth_utils import to_tuple

from trx_utils import (
    decode_hex,
    is_bytes,
    is_text,
    to_text
)

from tronapi.common.formatters import recursive_map
from tronapi.exceptions import FallbackNotFound
from tronapi.common.toolz import (
    curry,
    partial,
    pipe,
)

DYNAMIC_TYPES = ['bytes', 'string']

INT_SIZES = range(8, 257, 8)
BYTES_SIZES = range(1, 33)
UINT_TYPES = ['uint{0}'.format(i) for i in INT_SIZES]
INT_TYPES = ['int{0}'.format(i) for i in INT_SIZES]
BYTES_TYPES = ['bytes{0}'.format(i) for i in BYTES_SIZES] + ['bytes32.byte']

STATIC_TYPES = list(itertools.chain(
    ['address', 'bool'],
    UINT_TYPES,
    INT_TYPES,
    BYTES_TYPES,
))

BASE_TYPE_REGEX = '|'.join((
    _type + '(?![a-z0-9])'
    for _type
    in itertools.chain(STATIC_TYPES, DYNAMIC_TYPES)
))

SUB_TYPE_REGEX = (
    r'\['
    '[0-9]*'
    r'\]'
)

TYPE_REGEX = (
    '^'
    '(?:{base_type})'
    '(?:(?:{sub_type})*)?'
    '$'
).format(
    base_type=BASE_TYPE_REGEX,
    sub_type=SUB_TYPE_REGEX,
)

NAME_REGEX = (
    '[a-zA-Z_]'
    '[a-zA-Z0-9_]*'
)

ENUM_REGEX = (
    '^'
    '{lib_name}'
    r'\.'
    '{enum_name}'
    '$'
).format(lib_name=NAME_REGEX, enum_name=NAME_REGEX)

END_BRACKETS_OF_ARRAY_TYPE_REGEX = r"\[[^]]*\]$"

NAME_REGEX = (
    '[a-zA-Z_]'
    '[a-zA-Z0-9_]*'
)

ARRAY_REGEX = (
    "^"
    "[a-zA-Z0-9_]+"
    "({sub_type})+"
    "$"
).format(sub_type=SUB_TYPE_REGEX)


def filter_by_argument_name(argument_names, contract_abi):
    return [
        abi
        for abi in contract_abi
        if set(argument_names).intersection(
            get_abi_input_names(abi)
        ) == set(argument_names)
    ]


try:
    from eth_abi.abi import (
        process_type,
        collapse_type,
    )
except ImportError:
    from eth_abi.grammar import (
        parse as parse_type_string,
        normalize as normalize_type_string,
        TupleType,
    )


    def process_type(type_str):
        normalized_type_str = normalize_type_string(type_str)
        abi_type = parse_type_string(normalized_type_str)

        if isinstance(abi_type, TupleType):
            type_str_repr = repr(type_str)
            if type_str != normalized_type_str:
                type_str_repr = '{} (normalized to {})'.format(
                    type_str_repr,
                    repr(normalized_type_str),
                )

            raise ValueError(
                "Cannot process type {}: tuple types not supported".format(
                    type_str_repr,
                )
            )

        abi_type.validate()

        sub = abi_type.sub
        if isinstance(sub, tuple):
            sub = 'x'.join(map(str, sub))
        elif isinstance(sub, int):
            sub = str(sub)
        else:
            sub = ''

        arrlist = abi_type.arrlist
        if isinstance(arrlist, tuple):
            arrlist = list(map(list, arrlist))
        else:
            arrlist = []

        return abi_type.base, sub, arrlist


    def collapse_type(base, sub, arrlist):
        return base + str(sub) + ''.join(map(repr, arrlist))


def filter_by_type(_type, contract_abi):
    return [abi for abi in contract_abi if abi['type'] == _type]


def filter_by_name(name, contract_abi):
    return [
        abi
        for abi
        in contract_abi
        if (
                abi['type'] not in ('fallback', 'constructor') and
                abi['name'] == name
        )
    ]


def get_abi_input_types(abi):
    if 'inputs' not in abi and abi['type'] == 'fallback':
        return []
    else:
        return [arg['type'] for arg in abi['inputs']]


def get_abi_output_types(abi):
    if abi['type'] == 'fallback':
        return []
    else:
        return [arg['type'] for arg in abi['outputs']]


def get_abi_input_names(abi):
    if 'inputs' not in abi and abi['type'] == 'fallback':
        return []
    else:
        return [arg['name'] for arg in abi['inputs']]


def length_of_array_type(abi_type):
    if not is_array_type(abi_type):
        raise ValueError(
            "Cannot parse length of nonarray abi-type: {0}".format(abi_type)
        )

    inner_brackets = re.search(END_BRACKETS_OF_ARRAY_TYPE_REGEX, abi_type).group(0).strip("[]")
    if not inner_brackets:
        return None
    else:
        return int(inner_brackets)


def get_fallback_func_abi(contract_abi):
    fallback_abis = filter_by_type('fallback', contract_abi)
    if fallback_abis:
        return fallback_abis[0]
    else:
        raise FallbackNotFound("No fallback function was found in the contract ABI.")


def fallback_func_abi_exists(contract_abi):
    return filter_by_type('fallback', contract_abi)


def get_constructor_abi(contract_abi):
    candidates = [
        abi for abi in contract_abi if abi['type'] == 'constructor'
    ]
    if len(candidates) == 1:
        return candidates[0]
    elif len(candidates) == 0:
        return None
    elif len(candidates) > 1:
        raise ValueError("Found multiple constructors.")


def is_recognized_type(abi_type):
    return bool(re.match(TYPE_REGEX, abi_type))


def is_bool_type(abi_type):
    return abi_type == 'bool'


def is_uint_type(abi_type):
    return abi_type in UINT_TYPES


def is_int_type(abi_type):
    return abi_type in INT_TYPES


def is_address_type(abi_type):
    return abi_type == 'address'


def is_bytes_type(abi_type):
    return abi_type in BYTES_TYPES + ['bytes']


def is_string_type(abi_type):
    return abi_type == 'string'


@curry
def is_length(target_length, value):
    return len(value) == target_length


def size_of_type(abi_type):
    """
    Returns size in bits of abi_type
    """
    if 'string' in abi_type:
        return None
    if 'byte' in abi_type:
        return None
    if '[' in abi_type:
        return None
    if abi_type == 'bool':
        return 8
    if abi_type == 'address':
        return 160
    return int(re.sub(r"\D", "", abi_type))


def is_array_type(abi_type):
    return bool(re.match(ARRAY_REGEX, abi_type))


def sub_type_of_array_type(abi_type):
    if not is_array_type(abi_type):
        raise ValueError(
            "Cannot parse subtype of nonarray abi-type: {0}".format(abi_type)
        )

    return re.sub(END_BRACKETS_OF_ARRAY_TYPE_REGEX, '', abi_type, 1)


def is_probably_enum(abi_type):
    return bool(re.match(ENUM_REGEX, abi_type))


@to_tuple
def normalize_event_input_types(abi_args):
    for arg in abi_args:
        if is_recognized_type(arg['type']):
            yield arg
        elif is_probably_enum(arg['type']):
            yield {k: 'uint8' if k == 'type' else v for k, v in arg.items()}
        else:
            yield arg


def abi_to_signature(abi):
    function_signature = "{fn_name}({fn_input_types})".format(
        fn_name=abi['name'],
        fn_input_types=','.join([
            arg['type'] for arg in normalize_event_input_types(abi.get('inputs', []))
        ]),
    )
    return function_signature


def filter_by_argument_count(num_arguments, contract_abi):
    return [
        abi
        for abi
        in contract_abi
        if len(abi['inputs']) == num_arguments
    ]


def filter_by_encodability(args, kwargs, contract_abi):
    return [
        function_abi
        for function_abi
        in contract_abi
        if check_if_arguments_can_be_encoded(function_abi, args, kwargs)
    ]

class AcceptsHexStrMixin:
    def validate_value(self, value):
        if is_text(value):
            try:
                value = decode_hex(value)
            except binascii.Error:
                self.invalidate_value(
                    value,
                    msg='invalid hex string',
                )

        super().validate_value(value)


class ByteStringEncoder(AcceptsHexStrMixin, encoding.ByteStringEncoder):
    pass


class BytesEncoder(AcceptsHexStrMixin, encoding.BytesEncoder):
    pass


class TextStringEncoder(encoding.TextStringEncoder):
    @classmethod
    def validate_value(cls, value):
        if is_bytes(value):
            try:
                value = to_text(value)
            except UnicodeDecodeError:
                cls.invalidate_value(
                    value,
                    msg='not decodable as unicode string',
                )

        super().validate_value(value)


# We make a copy here just to make sure that eth-abi's default registry is not
# affected by our custom encoder subclasses
registry = default_registry.copy()

registry.unregister('address')
registry.unregister('bytes<M>')
registry.unregister('bytes')
registry.unregister('string')

registry.register(
    BaseEquals('bytes', with_sub=True),
    BytesEncoder, decoding.BytesDecoder,
    label='bytes<M>',
)

registry.register(
    BaseEquals('bytes', with_sub=False),
    ByteStringEncoder, decoding.ByteStringDecoder,
    label='bytes',
)
registry.register(
    BaseEquals('string'),
    TextStringEncoder, decoding.StringDecoder,
    label='string',
)

codec = ABICodec(registry)
is_encodable = codec.is_encodable


def check_if_arguments_can_be_encoded(function_abi, args, kwargs):
    try:
        arguments = merge_args_and_kwargs(function_abi, args, kwargs)
    except TypeError:
        return False

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

    types = get_abi_input_types(function_abi)

    return all(
        is_encodable(_type, arg)
        for _type, arg in zip(types, arguments)
    )


def merge_args_and_kwargs(function_abi, args, kwargs):
    if len(args) + len(kwargs) != len(function_abi.get('inputs', [])):
        raise TypeError(
            "Incorrect argument count.  Expected '{0}'.  Got '{1}'".format(
                len(function_abi['inputs']),
                len(args) + len(kwargs),
            )
        )

    if not kwargs:
        return args

    args_as_kwargs = {
        arg_abi['name']: arg
        for arg_abi, arg in zip(function_abi['inputs'], args)
    }
    duplicate_keys = set(args_as_kwargs).intersection(kwargs.keys())
    if duplicate_keys:
        raise TypeError(
            "{fn_name}() got multiple values for argument(s) '{dups}'".format(
                fn_name=function_abi['name'],
                dups=', '.join(duplicate_keys),
            )
        )

    sorted_arg_names = [arg_abi['name'] for arg_abi in function_abi['inputs']]

    unknown_kwargs = {key for key in kwargs.keys() if key not in sorted_arg_names}
    if unknown_kwargs:
        if function_abi.get('name'):
            raise TypeError(
                "{fn_name}() got unexpected keyword argument(s) '{dups}'".format(
                    fn_name=function_abi.get('name'),
                    dups=', '.join(unknown_kwargs),
                )
            )
        # show type instead of name in the error message incase key 'name' is missing.
        raise TypeError(
            "Type: '{_type}' got unexpected keyword argument(s) '{dups}'".format(
                _type=function_abi.get('type'),
                dups=', '.join(unknown_kwargs),
            )
        )

    sorted_args = list(zip(
        *sorted(
            itertools.chain(kwargs.items(), args_as_kwargs.items()),
            key=lambda kv: sorted_arg_names.index(kv[0])
        )
    ))
    if sorted_args:
        return sorted_args[1]
    else:
        return tuple()


def abi_sub_tree(data_type, data_value):
    if data_type is None:
        return ABITypedData([None, data_value])

    try:
        base, sub, arrlist = data_type
    except ValueError:
        base, sub, arrlist = process_type(data_type)

    collapsed = collapse_type(base, sub, arrlist)

    if arrlist:
        sub_type = (base, sub, arrlist[:-1])
        return ABITypedData([
            collapsed,
            [
                abi_sub_tree(sub_type, sub_value)
                for sub_value in data_value
            ],
        ])
    else:
        return ABITypedData([collapsed, data_value])


@curry
def map_abi_data(normalizers, types, data):
    """
        This function will apply normalizers to your data, in the
    context of the relevant types. Each normalizer is in the format:

    def normalizer(datatype, data):
        # Conditionally modify data
        return (datatype, data)

    Where datatype is a valid ABI type string, like "uint".

    In case of an array, like "bool[2]", normalizer will receive `data`
    as an iterable of typed data, like `[("bool", True), ("bool", False)]`.

    Internals
    ---

    This is accomplished by:

    1. Decorating the data tree with types
    2. Recursively mapping each of the normalizers to the data
    3. Stripping the types back out of the tree
    """

    pipeline = itertools.chain(
        [abi_data_tree(types)],
        map(data_tree_map, normalizers),
        [partial(recursive_map, strip_abi_type)],
    )

    return pipe(data, *pipeline)


@curry
def abi_data_tree(types, data):
    """Decorate the data tree with pairs of (type, data). The pair tuple is actually an
    ABITypedData, but can be accessed as a tuple.

    Examples:
        >>> abi_data_tree(types=["bool[2]", "uint"], data=[[True, False], 0])

    Returns:
        [("bool[2]", [("bool", True), ("bool", False)]), ("uint256", 0)]
    """

    return [
        abi_sub_tree(data_type, data_value)
        for data_type, data_value
        in zip(types, data)
    ]


@curry
def data_tree_map(func, data_tree):
    """
    Map func to every ABITypedData element in the tree. func will
    receive two args: abi_type, and data
    """

    def map_to_typed_data(elements):
        if isinstance(elements, ABITypedData) and elements.abi_type is not None:
            return ABITypedData(func(*elements))
        else:
            return elements

    return recursive_map(map_to_typed_data, data_tree)


class ABITypedData(namedtuple('ABITypedData', 'abi_type, data')):
    """
    This class marks data as having a certain ABI-type.

    >>> a1 = ABITypedData(['address', addr1])
    >>> a2 = ABITypedData(['address', addr2])
    >>> addrs = ABITypedData(['address[]', [a1, a2])

    You can access the fields using tuple() interface, or with
    attributes:

    >>> assert a1.abi_type == a1[0]
    >>> assert a1.data == a1[1]

    Unlike a typical `namedtuple`, you initialize with a single
    positional argument that is iterable, to match the init
    interface of all other relevant collections.
    """

    def __new__(cls, iterable):
        return super().__new__(cls, *iterable)


def strip_abi_type(elements):
    if isinstance(elements, ABITypedData):
        return elements.data
    else:
        return elements


================================================
FILE: tronapi/common/account.py
================================================
# --------------------------------------------------------------------
# Copyright (c) iEXBase. All rights reserved.
# Licensed under the MIT License.
# See License.txt in the project root for license information.
# --------------------------------------------------------------------

import codecs
from binascii import unhexlify

import base58
import ecdsa
from eth_keys import KeyAPI
from eth_account import Account as ETHAccount
from trx_utils import is_hex, is_bytes

from tronapi.common.datastructures import AttributeDict


class Account:
    @staticmethod
    def create():
        generate_key = ecdsa.SigningKey.generate(curve=ecdsa.SECP256k1)
        return PrivateKey(generate_key.to_string().hex())

    @staticmethod
    def sign_hash(message_hash, private_key):
        if not is_hex(message_hash):
            raise ValueError('Invalid message_hash provided')

        return ETHAccount.signHash(message_hash, private_key)

    @staticmethod
    def recover_hash(message_hash, signature):
        if not is_hex(message_hash):
            raise ValueError('Invalid message_hash provided')

        return ETHAccount.recoverHash(message_hash, signature=signature)


class Address(object):
    @staticmethod
    def from_hex(address):
        """Helper function that will convert a generic value from hex"""
        if not is_hex(address):
            return address

        return base58.b58encode_check(bytes.fromhex(address))

    @staticmethod
    def to_hex(address):
        """Helper function that will convert a generic value to hex"""
        if is_hex(address):
            return address.lower().replace('0x', '41', 2)

        return base58.b58decode_check(address).hex().upper()

    @staticmethod
    def from_private_key(private_key):
        return PrivateKey(private_key).address


class PrivateKey(object):
    def __init__(self, private_key):
        """Work with private key.
        Getting: PublicKey, PublicToAddress

        Example:::
            PrivateKey("4d1bc37b069b9f2e975c37770b7c87185dc3a10454e3ea024ce1fce8f3eb78bf")
        """
        _private = unhexlify(bytes(private_key, encoding='utf8'))
        self._key = KeyAPI.PrivateKey(_private)
        _length = len(self._key)

        # Key length must not exceed 64 length
        if _length < 64:
            raise ValueError('Key length must not exceed 64 length')

    @property
    def private_key(self):
        _raw_key = self._key.to_bytes()
        return codecs.decode(codecs.encode(_raw_key, 'hex'), 'ascii')

    @property
    def public_key(self) -> str:
        public_key = self._key.public_key
        return '04' + str(public_key)[2:]

    @property
    def address(self):
        public_key = self._key.public_key
        address = '41' + public_key.to_address()[2:]
        to_base58 = base58.b58encode_check(bytes.fromhex(address))

        # If bytecode then convert to string
        if is_bytes(to_base58):
            to_base58 = to_base58.decode()

        return AttributeDict({
            'hex': address,
            'base58': to_base58
        })

    def __str__(self):
        return self.private_key

    def __bytes__(self):
        return self._key.to_bytes()


================================================
FILE: tronapi/common/blocks.py
================================================
# --------------------------------------------------------------------------------------------
# Copyright (c) iEXBase. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
from trx_utils import (
    is_string,
    remove_0x_prefix,
    is_hex,
    is_integer
)


def is_hex_encoded_block_hash(value):
    if not is_string(value):
        return False
    return len(remove_0x_prefix(value)) == 64 and is_hex(value)


def is_hex_encoded_block_number(value):
    if not is_string(value):
        return False
    elif is_hex_encoded_block_hash(value):
        return False
    try:
        value_as_int = int(value, 16)
    except ValueError:
        return False
    return 0 <= value_as_int < 2**256


def select_method_for_block(value, if_hash, if_number):
    if isinstance(value, bytes):
        return if_hash
    elif is_hex_encoded_block_hash(value):
        return if_hash
    elif is_integer(value) and (0 <= value < 2**256):
        return if_number
    elif is_hex_encoded_block_number(value):
        return if_number
    else:
        raise ValueError(
            "Value did not match any of the recognized block identifiers: {0}".format(value)
        )


================================================
FILE: tronapi/common/contracts.py
================================================
# --------------------------------------------------------------------
# Copyright (c) iEXBase. All rights reserved.
# Licensed under the MIT License.
# See License.txt in the project root for license information.
# --------------------------------------------------------------------

import functools

from hexbytes import HexBytes

from eth_utils import (
    to_hex,
    function_abi_to_4byte_selector
)
from trx_utils import is_text, encode_hex

from tronapi.common.abi import (
    filter_by_name,
    filter_by_encodability,
    filter_by_argument_count,
    get_fallback_func_abi,
    abi_to_signature,
    get_abi_input_types,
    check_if_arguments_can_be_encoded,
    map_abi_data,
    merge_args_and_kwargs
)

from tronapi.common.normalizers import (
    abi_address_to_hex,
    abi_bytes_to_bytes,
    abi_string_to_text
)
from tronapi.common.toolz import (
    pipe,
    valmap,
)
from eth_abi import (
    encode_abi as eth_abi_encode_abi,
)

from eth_abi.exceptions import (
    EncodingError,
)


class FallbackFn:
    pass


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

    if fn_identifier is FallbackFn:
        return get_fallback_func_abi(abi)

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

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

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

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

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


def encode_abi(tron, abi, arguments, data=None):
    argument_types = get_abi_input_types(abi)

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

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

        normalized_arguments = map_abi_data(
            normalizers,
            argument_types,
            arguments,
        )

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

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


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

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

    fn_selector = encode_hex(function_abi_to_4byte_selector(fn_abi))

    fn_arguments = merge_args_and_kwargs(fn_abi, args, kwargs)

    return fn_abi, fn_selector, fn_arguments


================================================
FILE: tronapi/common/datastructures.py
================================================
from collections import (
    Hashable,
    Mapping,
    MutableMapping,
    OrderedDict,
    Sequence,
)

from trx_utils import (
    is_integer,
)

from tronapi.common.formatters import (
    recursive_map,
)

# Hashable must be immutable:
# "the implementation of hashable collections requires that a key's hash value is immutable"
# https://docs.python.org/3/reference/datamodel.html#object.__hash__


class ReadableAttributeDict(Mapping):
    """
    The read attributes for the AttributeDict types
    """

    def __init__(self, dictionary, *args, **kwargs):
        self.__dict__ = dict(dictionary)
        self.__dict__.update(dict(*args, **kwargs))

    def __getitem__(self, key):
        return self.__dict__[key]

    def __iter__(self):
        return iter(self.__dict__)

    def __len__(self):
        return len(self.__dict__)

    def __repr__(self):
        return self.__class__.__name__ + "(%r)" % self.__dict__

    def _repr_pretty_(self, builder, cycle):
        """
        Custom pretty output for the IPython console
        """
        builder.text(self.__class__.__name__ + "(")
        if cycle:
            builder.text("<cycle>")
        else:
            builder.pretty(self.__dict__)
        builder.text(")")

    @classmethod
    def _apply_if_mapping(cls, value):
        if isinstance(value, Mapping):
            return cls(value)
        else:
            return value

    @classmethod
    def recursive(cls, value):
        return recursive_map(cls._apply_if_mapping, value)


class MutableAttributeDict(MutableMapping, ReadableAttributeDict):

    def __setitem__(self, key, val):
        self.__dict__[key] = val

    def __delitem__(self, key):
        del self.__dict__[key]


class AttributeDict(ReadableAttributeDict, Hashable):
    """
    This provides superficial immutability, someone could hack around it
    """

    def __setattr__(self, attr, val):
        if attr == '__dict__':
            super().__setattr__(attr, val)
        else:
            raise TypeError('This data is immutable -- create a copy instead of modifying')

    def __delattr__(self, key):
        raise TypeError('This data is immutable -- create a copy instead of modifying')

    def __hash__(self):
        return hash(tuple(sorted(self.items())))

    def __eq__(self, other):
        if isinstance(other, Mapping):
            return self.__dict__ == dict(other)
        else:
            return False


class NamedElementOnion(Mapping):
    """
    Add layers to an onion-shaped structure. Optionally, inject to a specific layer.
    This structure is iterable, where the outermost layer is first, and innermost is last.
    """
    def __init__(self, init_elements, valid_element=callable):
        self._queue = OrderedDict()
        for element in reversed(init_elements):
            if valid_element(element):
                self.add(element)
            else:
                self.add(*element)

    def add(self, element, name=None):
        if name is None:
            name = element

        if name in self._queue:
            if name is element:
                raise ValueError("You can't add the same un-named instance twice")
            else:
                raise ValueError("You can't add the same name again, use replace instead")

        self._queue[name] = element

    def inject(self, element, name=None, layer=None):
        """
        Inject a named element to an arbitrary layer in the onion.

        The current implementation only supports insertion at the innermost layer,
        or at the outermost layer. Note that inserting to the outermost is equivalent
        to calling :meth:`add` .
        """
        if not is_integer(layer):
            raise TypeError("The layer for insertion must be an int.")
        elif layer != 0 and layer != len(self._queue):
            raise NotImplementedError(
                "You can only insert to the beginning or end of a %s, currently. "
                "You tried to insert to %d, but only 0 and %d are permitted. " % (
                    type(self),
                    layer,
                    len(self._queue),
                )
            )

        self.add(element, name=name)

        if layer == 0:
            if name is None:
                name = element
            self._queue.move_to_end(name, last=False)
        elif layer == len(self._queue):
            return
        else:
            raise AssertionError("Impossible to reach: earlier validation raises an error")

    def clear(self):
        self._queue.clear()

    def replace(self, old, new):
        if old not in self._queue:
            raise ValueError("You can't replace unless one already exists, use add instead")
        to_be_replaced = self._queue[old]
        if to_be_replaced is old:
            # re-insert with new name in old slot
            self._replace_with_new_name(old, new)
        else:
            self._queue[old] = new
        return to_be_replaced

    def remove(self, old):
        if old not in self._queue:
            raise ValueError("You can only remove something that has been added")
        del self._queue[old]

    def _replace_with_new_name(self, old, new):
        self._queue[new] = new
        found_old = False
        for key in list(self._queue.keys()):
            if not found_old:
                if key == old:
                    found_old = True
                continue
            elif key != new:
                self._queue.move_to_end(key)
        del self._queue[old]

    def __iter__(self):
        elements = self._queue.values()
        if not isinstance(elements, Sequence):
            elements = list(elements)
        return iter(reversed(elements))

    def __add__(self, other):
        if not isinstance(other, NamedElementOnion):
            raise NotImplementedError("You can only combine with another NamedElementOnion")
        combined = self._queue.copy()
        combined.update(other._queue)
        return NamedElementOnion(combined.items())

    def __contains__(self, element):
        return element in self._queue

    def __getitem__(self, element):
        return self._queue[element]

    def __len__(self):
        return len(self._queue)

    def __reversed__(self):
        elements = self._queue.values()
        if not isinstance(elements, Sequence):
            elements = list(elements)
        return iter(elements)


================================================
FILE: tronapi/common/datatypes.py
================================================
# --------------------------------------------------------------------
# Copyright (c) iEXBase. All rights reserved.
# Licensed under the MIT License.
# See License.txt in the project root for license information.
# --------------------------------------------------------------------

from tronapi.common.formatters import apply_formatters_to_dict
from tronapi.common.toolz import (
    concat,
    curry,
)


@curry
def verify_attr(class_name, key, namespace):
    if key not in namespace:
        raise AttributeError(
            "Property {0} not found on {1} class. "
            "`{1}.factory` only accepts keyword arguments which are "
            "present on the {1} class".format(key, class_name)
        )


class PropertyCheckingFactory(type):
    def __init__(cls, name, bases, namespace, **kargs):
        # see PEP487.  To accept kwargs in __new__, they need to be
        # filtered out here.
        super().__init__(name, bases, namespace)

    def __new__(mcs, name, bases, namespace, normalizers=None):
        all_bases = set(concat(base.__mro__ for base in bases))
        for key in namespace:
            verify_key_attr = verify_attr(name, key)
            verify_key_attr(concat(base.__dict__.keys() for base in all_bases))

        if normalizers:
            processed_namespace = apply_formatters_to_dict(
                normalizers,
                namespace)
        else:
            processed_namespace = namespace

        return super().__new__(mcs, name, bases, processed_namespace)


================================================
FILE: tronapi/common/encoding.py
================================================
import json
import re
from typing import Union

from eth_account.datastructures import AttributeDict
from hexbytes import HexBytes

from eth_utils import (
    hexstr_if_str,
    to_hex,
    big_endian_to_int,
    int_to_big_endian
)
from trx_utils import (
    remove_0x_prefix,
    encode_hex,
    add_0x_prefix,
    decode_hex,
    is_boolean,
    is_integer,
    is_list_like,
    is_bytes
)

from tronapi.common.abi import (
    size_of_type,
    sub_type_of_array_type,
    is_array_type,
    is_bool_type,
    is_uint_type,
    is_int_type,
    is_address_type,
    is_bytes_type,
    is_string_type
)


from tronapi.common.validation import (
    validate_abi_type,
    validate_abi_value
)

from tronapi.common.toolz import (
    curry
)

from tronapi.common.validation import assert_one_val


def hex_encode_abi_type(abi_type, value, force_size=None):
    """
    Encodes value into a hex string in format of abi_type
    """
    validate_abi_type(abi_type)
    validate_abi_value(abi_type, value)

    data_size = force_size or size_of_type(abi_type)
    if is_array_type(abi_type):
        sub_type = sub_type_of_array_type(abi_type)
        return "".join([remove_0x_prefix(hex_encode_abi_type(sub_type, v, 256)) for v in value])
    elif is_bool_type(abi_type):
        return to_hex_with_size(value, data_size)
    elif is_uint_type(abi_type):
        return to_hex_with_size(value, data_size)
    elif is_int_type(abi_type):
        return to_hex_twos_compliment(value, data_size)
    elif is_address_type(abi_type):
        return pad_hex(value, data_size)
    elif is_bytes_type(abi_type):
        if is_bytes(value):
            return encode_hex(value)
        else:
            return value
    elif is_string_type(abi_type):
        return to_hex(text=value)
    else:
        raise ValueError(
            "Unsupported ABI type: {0}".format(abi_type)
        )


def to_hex_twos_compliment(value, bit_size):
    """
    Converts integer value to twos compliment hex representation with given bit_size
    """
    if value >= 0:
        return to_hex_with_size(value, bit_size)

    value = (1 << bit_size) + value
    hex_value = hex(value)
    hex_value = hex_value.rstrip("L")
    return hex_value


def to_hex_with_size(value, bit_size):
    """Converts a value to hex with given bit_size:"""
    return pad_hex(to_hex(value), bit_size)


def pad_hex(value, bit_size):
    """Pads a hex string up to the given bit_size"""
    value = remove_0x_prefix(value)
    return add_0x_prefix(value.zfill(int(bit_size / 4)))


def trim_hex(hexstr):
    if hexstr.startswith('0x0'):
        hexstr = re.sub('^0x0+', '0x', hexstr)
        if hexstr == '0x':
            hexstr = '0x0'
    return hexstr


def to_int(value=None, hexstr=None, text=None):
    """Converts value to it's integer representation.

    Values are converted this way:

     * value:
       * bytes: big-endian integer
       * bool: True => 1, False => 0
     * hexstr: interpret hex as integer
     * text: interpret as string of digits, like '12' => 12
    """
    assert_one_val(value, hexstr=hexstr, text=text)

    if hexstr is not None:
        return int(hexstr, 16)
    elif text is not None:
        return int(text)
    elif isinstance(value, bytes):
        return big_endian_to_int(value)
    elif isinstance(value, str):
        raise TypeError("Pass in strings with keyword hexstr or text")
    else:
        return int(value)


@curry
def text_if_str(to_type, text_or_primitive):
    """Convert to a type, assuming that strings can be only unicode text (not a hexstr)"""
    if isinstance(text_or_primitive, str):
        (primitive, text) = (None, text_or_primitive)
    else:
        (primitive, text) = (text_or_primitive, None)
    return to_type(primitive, text=text)


def to_text(primitive=None, hexstr=None, text=None):
    assert_one_val(primitive, hexstr=hexstr, text=text)

    if hexstr is not None:
        return to_bytes(hexstr=hexstr).decode('utf-8')
    elif text is not None:
        return text
    elif isinstance(primitive, str):
        return to_text(hexstr=primitive)
    elif isinstance(primitive, bytes):
        return primitive.decode('utf-8')
    elif is_integer(primitive):
        byte_encoding = int_to_big_endian(primitive)
        return to_text(byte_encoding)
    raise TypeError("Expected an int, bytes or hexstr.")


def to_bytes(primitive=None, hexstr=None, text=None):
    assert_one_val(primitive, hexstr=hexstr, text=text)

    if is_boolean(primitive):
        return b'\x01' if primitive else b'\x00'
    elif isinstance(primitive, bytes):
        return primitive
    elif is_integer(primitive):
        return to_bytes(hexstr=to_hex(primitive))
    elif hexstr is not None:
        if len(hexstr) % 2:
            hexstr = '0x0' + remove_0x_prefix(hexstr)
        return decode_hex(hexstr)
    elif text is not None:
        return text.encode('utf-8')
    raise TypeError("expected an int in first arg, or keyword of hexstr or text")


def to_4byte_hex(hex_or_str_or_bytes: Union[int, str, bytes]) -> str:
    size_of_4bytes = 4 * 8
    byte_str = hexstr_if_str(to_bytes, hex_or_str_or_bytes)
    if len(byte_str) > 4:
        raise ValueError(
            'expected value of size 4 bytes. Got: %d bytes' % len(byte_str)
        )
    hex_str = encode_hex(byte_str)
    return pad_hex(hex_str, size_of_4bytes)


class FriendlyJsonSerialize:
    """
    Friendly JSON serializer & deserializer
    When encoding or decoding fails, this class collects
    information on which fields failed, to show more
    helpful information in the raised error messages.
    """

    def _json_mapping_errors(self, mapping):
        for key, val in mapping.items():
            try:
                self._friendly_json_encode(val)
            except TypeError as exc:
                yield "%r: because (%s)" % (key, exc)

    def _json_list_errors(self, iterable):
        for index, element in enumerate(iterable):
            try:
                self._friendly_json_encode(element)
            except TypeError as exc:
                yield "%d: because (%s)" % (index, exc)

    def _friendly_json_encode(self, obj, cls=None):
        try:
            encoded = json.dumps(obj, cls=cls)
            return encoded
        except TypeError as full_exception:
            if hasattr(obj, 'items'):
                item_errors = '; '.join(self._json_mapping_errors(obj))
                raise TypeError("dict had unencodable value at keys: {{{}}}".format(item_errors))
            elif is_list_like(obj):
                element_errors = '; '.join(self._json_list_errors(obj))
                raise TypeError("list had unencodable value at index: [{}]".format(element_errors))
            else:
                raise full_exception

    @staticmethod
    def json_decode(json_str):
        try:
            decoded = json.loads(json_str)
            return decoded
        except json.decoder.JSONDecodeError as exc:
            err_msg = 'Could not decode {} because of {}.'.format(repr(json_str), exc)
            # Calling code may rely on catching JSONDecodeError to recognize bad json
            # so we have to re-raise the same type.
            raise json.decoder.JSONDecodeError(err_msg, exc.doc, exc.pos)

    def json_encode(self, obj, cls=None):
        try:
            return self._friendly_json_encode(obj, cls=cls)
        except TypeError as exc:
            raise TypeError("Could not encode to JSON: {}".format(exc))


class TronJsonEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, AttributeDict):
            return {k: v for k, v in obj.items()}
        if isinstance(obj, HexBytes):
            return obj.hex()
        return json.JSONEncoder.default(self, obj)


def to_json(obj: object) -> object:
    """Convert a complex object (like a transaction object) to a JSON string"""
    return FriendlyJsonSerialize().json_encode(obj, cls=TronJsonEncoder)


================================================
FILE: tronapi/common/formatters.py
================================================
# --------------------------------------------------------------------
# Copyright (c) iEXBase. All rights reserved.
# Licensed under the MIT License.
# See License.txt in the project root for license information.
# --------------------------------------------------------------------

from collections import Mapping, Iterable

from eth_utils import to_dict
from trx_utils import (
    reject_recursive_repeats,
    is_string
)

from tronapi.common.toolz import (
    curry
)


@curry
@to_dict
def apply_formatters_to_dict(formatters, value):
    for key, item in value.items():
        if key in formatters:
            try:
                yield key, formatters[key](item)
            except (TypeError, ValueError) as exc:
                raise type(exc)("Could not format value %r as field %r" % (item, key)) from exc
        else:
            yield key, item


@reject_recursive_repeats
def recursive_map(func, data):
    """
    Apply func to data, and any collection items inside data (using map_collection).
    Define func so that it only applies to the type of value that you want it to apply to.
    """

    def recurse(item):
        return recursive_map(func, item)

    items_mapped = map_collection(recurse, data)
    return func(items_mapped)


def map_collection(func, collection):
    """
    Apply func to each element of a collection, or value of a dictionary.
    If the value is not a collection, return it unmodified
    """
    datatype = type(collection)
    if isinstance(collection, Mapping):
        return datatype((key, func(val)) for key, val in collection.items())
    if is_string(collection):
        return collection
    elif isinstance(collection, Iterable):
        return datatype(map(func, collection))
    else:
        return collection


================================================
FILE: tronapi/common/normalizers.py
================================================
import functools
import json

from eth_utils import (
    is_binary_address,
    to_hex,
    hexstr_if_str
)
from hexbytes import HexBytes
from toolz import curry

from tronapi.common.abi import process_type
from tronapi.common.account import Address
from tronapi.common.encoding import (
    to_bytes,
    text_if_str,
    to_text
)
from tronapi.common.validation import (
    validate_abi,
    validate_address
)


def implicitly_identity(to_wrap):
    @functools.wraps(to_wrap)
    def wrapper(abi_type, data):
        modified = to_wrap(abi_type, data)
        if modified is None:
            return abi_type, data
        else:
            return modified

    return wrapper


def normalize_abi(abi):
    if isinstance(abi, str):
        abi = json.loads(abi)
    validate_abi(abi)
    return abi


def normalize_bytecode(bytecode):
    if bytecode:
        bytecode = HexBytes(bytecode)
    return bytecode


@implicitly_identity
def abi_address_to_hex(abi_type, data):
    if abi_type == 'address':
        validate_address(data)
        if is_binary_address(data):
            return abi_type, to_hex(data)


@implicitly_identity
def abi_string_to_text(abi_type, data):
    if abi_type == 'string':
        return abi_type, text_if_str(to_text, data)


@implicitly_identity
def abi_bytes_to_bytes(abi_type, data):
    base, sub, arrlist = process_type(abi_type)
    if base == 'bytes' and not arrlist:
        return abi_type, hexstr_if_str(to_bytes, data)


@implicitly_identity
def addresses_checksummed(abi_type, data):
    if abi_type == 'address':
        return abi_type, to_checksum_address(data)


def to_checksum_address(address: str):
    return Address().from_hex(address)


@curry
def abi_resolver(abi_type, val):
    return abi_type, val


BASE_RETURN_NORMALIZERS = [
    addresses_checksummed,
]


================================================
FILE: tronapi/common/threads.py
================================================
"""
A minimal implementation of the various gevent APIs used within this codebase.
"""
import threading
import time


class Timeout(Exception):
    """
    A limited subset of the `gevent.Timeout` context manager.
    """
    seconds = None
    exception = None
    begun_at = None
    is_running = None

    def __init__(self, seconds=None, exception=None, *args, **kwargs):
        self.seconds = seconds
        self.exception = exception

    def __enter__(self):
        self.start()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        return False

    def __str__(self):
        if self.seconds is None:
            return ''
        return "{0} seconds".format(self.seconds)

    @property
    def expire_at(self):
        if self.seconds is None:
            raise ValueError("Timeouts with `seconds == None` do not have an expiration time")
        elif self.begun_at is None:
            raise ValueError("Timeout has not been started")
        return self.begun_at + self.seconds

    def start(self):
        if self.is_running is not None:
            raise ValueError("Timeout has already been started")
        self.begun_at = time.time()
        self.is_running = True

    def check(self):
        if self.is_running is None:
            raise ValueError("Timeout has not been started")
        elif self.is_running is False:
            raise ValueError("Timeout has already been cancelled")
        elif self.seconds is None:
            return
        elif time.time() > self.expire_at:
            self.is_running = False
            if isinstance(self.exception, type):
                raise self.exception(str(self))
            elif isinstance(self.exception, Exception):
                raise self.exception
            else:
                raise self

    def cancel(self):
        self.is_running = False

    def sleep(self, seconds):
        time.sleep(seconds)
        self.check()


class ThreadWithReturn(threading.Thread):
    def __init__(self, target=None, args=None, kwargs=None):
        super().__init__(
            target=target,
            args=args or tuple(),
            kwargs=kwargs or {},
        )
        self.target = target
        self.args = args
        self.kwargs = kwargs

    def run(self):
        self._return = self.target(*self.args, **self.kwargs)

    def get(self, timeout=None):
        self.join(timeout)
        try:
            return self._return
        except AttributeError:
            raise RuntimeError("Something went wrong.  No `_return` property was set")


class TimerClass(threading.Thread):
    def __init__(self, interval, callback, *args):
        threading.Thread.__init__(self)
        self.callback = callback
        self.terminate_event = threading.Event()
        self.interval = interval
        self.args = args

    def run(self):
        while not self.terminate_event.is_set():
            self.callback(*self.args)
            self.terminate_event.wait(self.interval)

    def stop(self):
        self.terminate_event.set()


def spawn(target, *args, thread_class=ThreadWithReturn, **kwargs):
    thread = thread_class(
        target=target,
        args=args,
        kwargs=kwargs,
    )
    thread.daemon = True
    thread.start()
    return thread


================================================
FILE: tronapi/common/toolz/__init__.py
================================================
# --------------------------------------------------------------------
# Copyright (c) iEXBase. All rights reserved.
# Licensed under the MIT License.
# See License.txt in the project root for license information.
# --------------------------------------------------------------------

try:
    from cytoolz import (
        assoc,
        complement,
        compose,
        concat,
        curry,
        dicttoolz,
        dissoc,
        excepts,
        functoolz,
        groupby,
        identity,
        itertoolz,
        merge,
        partial,
        pipe,
        sliding_window,
        valfilter,
        valmap,
    )
except ImportError:
    from toolz import (  # noqa: F401
        assoc,
        complement,
        compose,
        concat,
        curry,
        dicttoolz,
        dissoc,
        excepts,
        functoolz,
        groupby,
        identity,
        itertoolz,
        merge,
        partial,
        pipe,
        sliding_window,
        valfilter,
        valmap,
    )


================================================
FILE: tronapi/common/transactions.py
================================================
from tronapi.common.threads import Timeout


def wait_for_transaction_id(tron, tx_id, timeout=120, poll_latency=0.1):
    with Timeout(timeout) as _timeout:
        while True:
            tx_detail = tron.trx.get_transaction(tx_id)
            # FIXME: The check for a null `ref_block_hash` is due to parity's
            if tx_detail is not None and \
                    'raw_data' in tx_detail and \
                    tx_detail['raw_data']['ref_block_hash'] is not None:
                break
            _timeout.sleep(poll_latency)
    return tx_detail


================================================
FILE: tronapi/common/validation.py
================================================
# --------------------------------------------------------------------------------------------
# Copyright (c) iEXBase. All rights reserved.
# Licensed under the MIT License.
# See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
import itertools
import re
from _sha256 import sha256
from typing import Any

import base58
from eth_utils import (
    function_abi_to_4byte_selector,
    apply_formatter_to_array
)

from trx_utils import (
    is_hex,
    encode_hex,
    is_0x_prefixed,
    is_text,
    is_list_like,
    is_dict,
    is_string,
    is_bytes,
    is_boolean,
    is_integer,
    is_binary_address, is_hex_address, is_checksum_address)

from tronapi.common.toolz import (
    compose,
    groupby,
    valfilter,
    valmap,
)

from tronapi.common.abi import filter_by_type, abi_to_signature, is_recognized_type, is_string_type, is_bytes_type, \
    is_address_type, is_int_type, is_uint_type, is_bool_type, sub_type_of_array_type, is_array_type, \
    length_of_array_type
from tronapi.exceptions import InvalidAddress


def _prepare_selector_collision_msg(duplicates):
    dup_sel = valmap(apply_formatter_to_array(abi_to_signature), duplicates)
    joined_funcs = valmap(lambda f: ', '.join(f), dup_sel)
    func_sel_msg_list = [funcs + ' have selector ' + sel for sel, funcs in joined_funcs.items()]
    return ' and\n'.join(func_sel_msg_list)


def is_valid_url(value):
    """Return whether or not given value is a valid URL.

    Args:
        value(str): URL address string to validate
    """
    regex = re.compile(
        r'^(?:http|ftp)s?://'
        r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|'
        r'localhost|'
        r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|'
        r'\[?[A-F0-9]*:[A-F0-9:]+\]?)'
        r'(?::\d+)?'
        r'(?:/?|[/?]\S+)$', re.IGNORECASE)

    result = regex.match(value)
    return bool(result)


def validate_abi(abi):
    """
    Helper function for validating an ABI
    """
    if not is_list_like(abi):
        raise ValueError("'abi' is not a list")

    if not all(is_dict(e) for e in abi):
        raise ValueError("'abi' is not a list of dictionaries")

    functions = filter_by_type('function', abi)
    selectors = groupby(
        compose(encode_hex, function_abi_to_4byte_selector),
        functions
    )
    duplicates = valfilter(lambda funcs: len(funcs) > 1, selectors)
    if duplicates:
        raise ValueError(
            'Abi contains functions with colliding selectors. '
            'Functions {0}'.format(_prepare_selector_collision_msg(duplicates))
        )


def validate_abi_type(abi_type):
    """
    Helper function for validating an abi_type
    """
    if not is_recognized_type(abi_type):
        raise ValueError("Unrecognized abi_type: {abi_type}".format(abi_type=abi_type))


def validate_abi_value(abi_type, value):
    """
    Helper function for validating a value against the expected abi_type
    Note: abi_type 'bytes' must either be python3 'bytes' object or ''
    """
    if is_array_type(abi_type) and is_list_like(value):
        # validate length
        specified_length = length_of_array_type(abi_type)
        if specified_length is not None:
            if specified_length < 1:
                raise TypeError(
                    "Invalid abi-type: {abi_type}. Length of fixed sized arrays"
                    "must be greater than 0."
                        .format(abi_type=abi_type)
                )
            if specified_length != len(value):
                raise TypeError(
                    "The following array length does not the length specified"
                    "by the abi-type, {abi_type}: {value}"
                        .format(abi_type=abi_type, value=value)
                )

        # validate sub_types
        sub_type = sub_type_of_array_type(abi_type)
        for v in value:
            validate_abi_value(sub_type, v)
        return
    elif is_bool_type(abi_type) and is_boolean(value):
        return
    elif is_uint_type(abi_type) and is_integer(value) and value >= 0:
        return
    elif is_int_type(abi_type) and is_integer(value):
        return
    elif is_address_type(abi_type):
        validate_address(value)
        return
    elif is_bytes_type(abi_type):
        if is_bytes(value):
            return
        elif is_string(value):
            if is_0x_prefixed(value):
                return
            else:
                raise TypeError(
                    "ABI values of abi-type 'bytes' must be either"
                    "a python3 'bytes' object or an '0x' prefixed string."
                )
    elif is_string_type(abi_type) and is_string(value):
        return

    raise TypeError(
        "The following abi value is not a '{abi_type}': {value}".format(abi_type=abi_type, value=value)
    )


def validate_address(value):
    """
    Helper function for validating an address
    """
    if is_bytes(value):
        if not is_binary_address(value):
            raise InvalidAddress("Address must be 20 bytes when input type is bytes", value)
        return

    if not isinstance(value, str):
        raise TypeError('Address {} must be provided as a string'.format(value))
    if not is_hex_address(value):
        raise InvalidAddress("Address must be 20 bytes, as a hex string with a 0x prefix", value)
    if not is_checksum_address(value):
        if value == value.lower():
            raise InvalidAddress(
                "Web3.py only accepts checksum addresses. "
                "The software that gave you this non-checksum address should be considered unsafe, "
                "please file it as a bug on their platform. "
                "Try using an ENS name instead. Or, if you must accept lower safety, "
                "use Web3.toChecksumAddress(lower_case_address).",
                value,
            )
        else:
            raise InvalidAddress(
                "Address has an invalid EIP-55 checksum. "
                "After looking up the address from the original source, try again.",
                value,
            )


def has_one_val(*args, **kwargs):
    vals = itertools.chain(args, kwargs.values())
    not_nones = list(filter(lambda val: val is not None, vals))
    return len(not_nones) == 1


def assert_one_val(*args, **kwargs):
    if not has_one_val(*args, **kwargs):
        raise TypeError(
            "Exactly one of the passed values can be specified. "
            "Instead, values were: %r, %r" % (args, kwargs)
        )


================================================
FILE: tronapi/constants.py
================================================
# --------------------------------------------------------------------
# Copyright (c) iEXBase. All rights reserved.
# Licensed under the MIT License.
# See License.txt in the project root for license information.
# --------------------------------------------------------------------

# Here we specify default values for the different needed urls.
# They are verified. Don't change this unless you know what you're doing.
DEFAULT_NODES = {
    'full_node': 'https://api.trongrid.io',
    'solidity_node': 'https://api.trongrid.io',
    'event_server': 'https://api.trongrid.io'
}


================================================
FILE: tronapi/contract.py
================================================
# --------------------------------------------------------------------
# Copyright (c) iEXBase. All rights reserved.
# Licensed under the MIT License.
# See License.txt in the project root for license information.
# --------------------------------------------------------------------

import copy

from eth_abi import decode_abi
from eth_utils import (
    function_abi_to_4byte_selector,
    to_hex
)
from hexbytes import HexBytes
from trx_utils import (
    encode_hex,
    is_text,
    deprecated_for,
    combomethod
)

from tronapi.common.abi import (
    filter_by_type,
    merge_args_and_kwargs,
    abi_to_signature,
    fallback_func_abi_exists,
    check_if_arguments_can_be_encoded,
    map_abi_data
)

from tronapi.common.contracts import (
    find_matching_fn_abi,
    encode_abi,
    get_function_info,
    FallbackFn
)
from tronapi.common.datatypes import PropertyCheckingFactory
from tronapi.common.encoding import to_4byte_hex
from tronapi.common.normalizers import (
    normalize_abi,
    normalize_bytecode,
    BASE_RETURN_NORMALIZERS
)
from tronapi.exceptions import (
    NoABIFunctionsFound,
    MismatchedABI,
    FallbackNotFound
)


class NonExistentFallbackFunction:
    @staticmethod
    def _raise_exception():
        raise FallbackNotFound("No fallback function was found in the contract ABI.")

    def __getattr__(self, attr):
        return NonExistentFallbackFunction._raise_exception


class ContractFunction:
    """Base class for contract functions"""
    address = None
    function_identifier = None
    tron = None
    contract_abi = None
    abi = None
    transaction = None
    arguments = None

    def __init__(self, abi=None):
        self.abi = abi
        self.fn_name = type(self).__name__

    def __call__(self, *args, **kwargs):
        clone = copy.copy(self)
        if args is None:
            clone.args = tuple()
        else:
            clone.args = args

        if kwargs is None:
            clone.kwargs = {}
        else:
            clone.kwargs = kwargs
        clone._set_function_info()
        return clone

    def _set_function_info(self):
        if not self.abi:
            self.abi = find_matching_fn_abi(
                self.contract_abi,
                self.function_identifier,
                self.args,
                self.kwargs
            )
        if self.function_identifier is FallbackFn:
            self.selector = encode_hex(b'')
        elif is_text(self.function_identifier):
            self.selector = encode_hex(function_abi_to_4byte_selector(self.abi))
        else:
            raise TypeError("Unsupported function identifier")

        self.arguments = merge_args_and_kwargs(self.abi, self.args, self.kwargs)

    @classmethod
    def factory(cls, class_name, **kwargs):
        return PropertyCheckingFactory(class_name, (cls,), kwargs)(kwargs.get('abi'))

    def __repr__(self):
        if self.abi:
            _repr = '<Function %s' % abi_to_signature(self.abi)
            if self.arguments is not None:
                _repr += ' bound to %r' % (self.arguments,)
            return _repr + '>'
        return '<Function %s>' % self.fn_name


class ContractFunctions:
    """Class containing contract function objects
    """

    def __init__(self, abi, tron, address=None):
        if abi:
            self.abi = abi
            self._functions = filter_by_type('function', self.abi)
            for func in self._functions:
                setattr(
                    self,
                    func['name'],
                    ContractFunction.factory(
                        func['name'],
                        tron=tron,
                        contract_abi=self.abi,
                        address=address,
                        function_identifier=func['name']))

    def __iter__(self):
        if not hasattr(self, '_functions') or not self._functions:
            return

        for func in self._functions:
            yield func['name']

    def __getattr__(self, function_name):
        if '_functions' not in self.__dict__:
            raise NoABIFunctionsFound(
                "The abi for this contract contains no function definitions. ",
                "Are you sure you provided the correct contract abi?"
            )
        elif function_name not in self.__dict__['_functions']:
            raise MismatchedABI(
                "The function '{}' was not found in this contract's abi. ".format(function_name),
                "Are you sure you provided the correct contract abi?"
            )
        else:
            return super().__getattribute__(function_name)

    def __getitem__(self, function_name):
        return getattr(self, function_name)


class Contract:
    # set during class construction
    tron = None

    # instance level properties
    address = None

    # class properties (overridable at instance level)
    abi = None

    bytecode = None
    bytecode_runtime = None

    functions = None
    events = None

    def __init__(self, address=None):
        """Create a new smart contract proxy object.
        :param address: Contract address as 0x hex string
        """
        if self.tron is None:
            raise AttributeError(
                'The `Contract` class has not been initialized.  Please use the '
                '`tron.contract` interface to create your contract class.'
            )

        if address:
            self.address = self.tron.address.to_hex(address)

        if not self.address:
            raise TypeError("The address argument is required to instantiate a contract.")

        self.functions = ContractFunctions(self.abi, self.tron, self.address)
        self.fallback = Contract.get_fallback_function(self.abi, self.tron, self.address)

    @classmethod
    def factory(cls, tron, class_name=None, **kwargs):

        kwargs['tron'] = tron
        normalizers = {
            'abi': normalize_abi,
            'bytecode': normalize_bytecode,
            'bytecode_runtime': normalize_bytecode,
        }

        contract = PropertyCheckingFactory(
            class_name or cls.__name__,
            (cls,),
            kwargs,
            normalizers=normalizers
        )

        setattr(contract, 'functions', ContractFunctions(contract.abi, contract.tron))
        setattr(contract, 'fallback', Contract.get_fallback_function(contract.abi, contract.tron))

        return contract

    @classmethod
    @deprecated_for("contract.constructor.transact")
    def deploy(cls, **kwargs):
        """Deploy Contract

        Deploys a contract.
        Returns TransactionExtention, which contains an unsigned transaction.

        Example:
        .. code-block:: python
            >>> MyContract.deploy(
                fee_limit=10**9,
                call_value=0,
                consume_user_resource_percent=10
            )

        Args:
            **kwargs: Transaction parameters for the deployment
            transaction as a dict

        """
        return cls.tron.transaction_builder.create_smart_contract(
            **kwargs,
            abi=cls.abi,
            bytecode=to_hex(cls.bytecode)
        )

    @classmethod
    def constructor(cls):
        if cls.bytecode is None:
            raise ValueError(
                "Cannot call constructor on a contract that does not have 'bytecode' associated "
                "with it"
            )
        return ContractConstructor(cls.tron,
                                   cls.abi,
                                   cls.bytecode)

    @combomethod
    def encodeABI(cls, fn_name, args=None, kwargs=None, data=None):
        """Encodes the arguments using the Tron ABI for the contract function
        that matches the given name and arguments..
        """
        fn_abi, fn_selector, fn_arguments = get_function_info(
            fn_name, contract_abi=cls.abi, args=args, kwargs=kwargs,
        )

        if data is None:
            data = fn_selector

        return encode_abi(cls.tron, fn_abi, fn_arguments, data)

    @staticmethod
    def get_fallback_function(abi, tron, address=None):
        if abi and fallback_func_abi_exists(abi):
            return ContractFunction.factory(
                'fallback',
                tron=tron,
                contract_abi=abi,
                address=address,
                function_identifier=FallbackFn)()

        return NonExistentFallbackFunction()

    @combomethod
    def all_functions(self):
        return find_functions_by_identifier(
            self.abi, self.tron, self.address, lambda _: True
        )

    @combomethod
    def get_function_by_signature(self, signature):
        if ' ' in signature:
            raise ValueError(
                'Function signature should not contain any spaces. '
                'Found spaces in input: %s' % signature
            )

        def callable_check(fn_abi):
            return abi_to_signature(fn_abi) == signature

        fns = find_functions_by_identifier(self.abi, self.tron, self.address, callable_check)
        return get_function_by_identifier(fns, 'signature')

    @combomethod
    def find_functions_by_name(self, fn_name):
        def callable_check(fn_abi):
            return fn_abi['name'] == fn_name

        return find_functions_by_identifier(
            self.abi, self.tron, self.address, callable_check
        )

    @combomethod
    def get_function_by_name(self, fn_name):
        fns = self.find_functions_by_name(fn_name)
        return get_function_by_identifier(fns, 'name')

    @combomethod
    def get_function_by_selector(self, selector):
        def callable_check(fn_abi):
            return encode_hex(function_abi_to_4byte_selector(fn_abi)) == to_4byte_hex(selector)

        fns = find_functions_by_identifier(self.abi, self.tron, self.address, callable_check)
        return get_function_by_identifier(fns, 'selector')

    @combomethod
    def decode_function_input(self, data):
        data = HexBytes(data)
        selector, params = data[:4], data[4:]
        func = self.get_function_by_selector(selector)
        names = [x['name'] for x in func.abi['inputs']]
        types = [x['type'] for x in func.abi['inputs']]
        decoded = decode_abi(types, params)
        normalized = map_abi_data(BASE_RETURN_NORMALIZERS, types, decoded)
        return func, dict(zip(names, normalized))

    @combomethod
    def find_functions_by_args(self, *args):
        def callable_check(fn_abi):
            return check_if_arguments_can_be_encoded(fn_abi, args=args, kwargs={})

        return find_functions_by_identifier(
            self.abi, self.tron, self.address, callable_check
        )

    @combomethod
    def get_function_by_args(self, *args):
        fns = self.find_functions_by_args(*args)
        return get_function_by_identifier(fns, 'args')


class ContractConstructor:
    """
    Class for contract constructor API.
    """

    def __init__(self, tron, abi, bytecode):
        self.tron = tron
        self.abi = abi
        self.bytecode = bytecode

    @staticmethod
    def check_forbidden_keys_in_transaction(transaction, forbidden_keys=None):
        keys_found = set(transaction.keys()) & set(forbidden_keys)
        if keys_found:
            raise ValueError("Cannot set {} in transaction".format(', '.join(keys_found)))

    @combomethod
    def transact(self, **kwargs):
        """Deploy Contract

        Deploys a contract.
        Returns TransactionExtention, which contains an unsigned transaction.

        Args:
            **kwargs: Additional options to send
        """

        return self.tron.transaction_builder.create_smart_contract(
            **kwargs,
            abi=self.abi,
            bytecode=to_hex(self.bytecode)
        )


def find_functions_by_identifier(contract_abi, tron, address, callable_check):
    fns_abi = filter_by_type('function', contract_abi)
    return [
        ContractFunction.factory(
            fn_abi['name'],
            tron=tron,
            contract_abi=contract_abi,
            address=address,
            function_identifier=fn_abi['name'],
            abi=fn_abi
        )
        for fn_abi in fns_abi
        if callable_check(fn_abi)
    ]


def get_function_by_identifier(fns, identifier):
    if len(fns) > 1:
        raise ValueError(
            'Found multiple functions with matching {0}. '
            'Found: {1!r}'.format(identifier, fns)
        )
    elif len(fns) == 0:
        raise ValueError(
            'Could not find any function with matching {0}'.format(identifier)
        )
    return fns[0]


================================================
FILE: tronapi/exceptions.py
================================================
# --------------------------------------------------------------------
# Copyright (c) iEXBase. All rights reserved.
# Licensed under the MIT License.
# See License.txt in the project root for license information.
# --------------------------------------------------------------------


class TronError(Exception):
    """Base class for TronAPI exceptions."""


class InvalidTronError(TronError):
    """Raised Tron Error"""


class FallbackNotFound(Exception):
    """
    Raised when fallback function doesn't exist in contract.
    """
    pass


class MismatchedABI(Exception):
    """
    Raised when an ABI does not match with supplied parameters, or when an
    attempt is made to access a function/event that does not exist in the ABI.
    """
    pass


class InvalidAddress(ValueError):
    """
    The supplied address does not have a valid checksum, as defined in EIP-55
    """
    pass


class NoABIFunctionsFound(AttributeError):
    """
    Raised when an ABI doesn't contain any functions.
    """
    pass


class ValidationError(Exception):
    """
    Raised when a supplied value is invalid.
    """
    pass


class TransportError(TronError):
    """Base exception for transport related errors.

    This is mainly for cases where the status code denotes an HTTP error, and
    for cases in which there was a connection error.

    """

    @property
    def status_code(self):
        return self.args[0]

    @property
    def error(self):
        return self.args[1]

    @property
    def info(self):
        return self.args[2]

    @property
    def url(self):
        return self.args[3]


class HttpError(TransportError):
    """Exception for errors occurring when connecting, and/or making a request"""


class BadRequest(TransportError):
    """Exception for HTTP 400 errors."""


class NotFoundError(TransportError):
    """Exception for HTTP 404 errors."""


class ServiceUnavailable(TransportError):
    """Exception for HTTP 503 errors."""


class GatewayTimeout(TransportError):
    """Exception for HTTP 503 errors."""


class TimeExhausted(Exception):
    """
    Raised when a method has not retrieved the desired result within a specified timeout.
    """
    pass


HTTP_EXCEPTIONS = {
    400: BadRequest,
    404: NotFoundError,
    503: ServiceUnavailable,
    504: GatewayTimeout,
}


================================================
FILE: tronapi/main.py
================================================
# -*- coding: utf-8 -*-
# --------------------------------------------------------------------
# Copyright (c) iEXBase. All rights reserved.
# Licensed under the MIT License.
# See License.txt in the project root for license information.
# --------------------------------------------------------------------

"""
    tronapi.main
    ===============

    Connect to the Tron network.

    :copyright: © 2019 by the iEXBase.
    :license: MIT License
"""

from eth_account.datastructures import AttributeDict
from urllib.parse import urlencode
from eth_utils import (
    apply_to_return_value,
    to_hex,
    keccak as tron_keccak,
)
from hexbytes import HexBytes
from trx_utils import (
    to_sun,
    from_sun,
    is_integer,
    add_0x_prefix,
    remove_0x_prefix,
    is_address
)

from tronapi.common.abi import map_abi_data


from tronapi.common.account import Address, PrivateKey, Account
from tronapi.common.normalizers import abi_resolver
from tronapi.common.encoding import (
    to_bytes,
    to_int,
    to_text,
    to_json,
    hex_encode_abi_type
)

from tronapi.exceptions import (
    InvalidTronError,
    TronError
)
from tronapi.manager import TronManager
from tronapi import HttpProvider, constants
from tronapi.transactionbuilder import TransactionBuilder
from tronapi.trx import Trx


DEFAULT_MODULES = {
    'trx': Trx
}


class Tron:
    # Providers
    HTTPProvider = HttpProvider

    _default_block = None
    _private_key = None
    _default_address = AttributeDict({})

    # Encoding and Decoding
    toBytes = staticmethod(to_bytes)
    toInt = staticmethod(to_int)
    toHex = staticmethod(to_hex)
    toText = staticmethod(to_text)
    toJSON = staticmethod(to_json)

    # Currency Utility
    toSun = staticmethod(to_sun)
    fromSun = staticmethod(from_sun)

    # Validate address
    isAddress = staticmethod(is_address)

    def __init__(self, **kwargs):
        """Connect to the Tron network.

        Args:
            kwargs (Any): We fill the most necessary parameters
            for working with blockchain Tron

        """

        # We check the obtained nodes, if the necessary parameters
        # are not specified, then we take the default
        kwargs.setdefault('full_node', constants.DEFAULT_NODES['full_node'])
        kwargs.setdefault('solidity_node', constants.DEFAULT_NODES['solidity_node'])
        kwargs.setdefault('event_server', constants.DEFAULT_NODES['event_server'])

        # The node manager allows you to automatically determine the node
        # on the router or manually refer to a specific node.
        # solidity_node, full_node or event_server
        self.manager = TronManager(self, dict(
            full_node=kwargs.get('full_node'),
            solidity_node=kwargs.get('solidity_node'),
            event_server=kwargs.get('event_server')
        ))

        # If the parameter of the private key is not empty,
        # then write to the variable
        if 'private_key' in kwargs:
            self.private_key = kwargs.get('private_key')

        # We check whether the default wallet address is set when
        # defining the class, and then written to the variable
        if 'default_address' in kwargs:
            self.default_address = kwargs.get('default_address')

        # If custom methods are not declared,
        # we take the default from the list
        modules = kwargs.setdefault('modules', DEFAULT_MODULES)
        for module_name, module_class in modules.items():
            module_class.attach(self, module_name)

        self.transaction_builder = TransactionBuilder(self)

    @property
    def default_block(self):
        return self._default_block

    @default_block.setter
    def default_block(self, block_id):
        """Sets the default block used as a reference for all future calls."""
        if block_id in ('latest', 'earliest', 0):
            self._default_block = block_id
            return

        if not is_integer(block_id) or not block_id:
            raise ValueError('Invalid block ID provided')

        self._default_block = abs(block_id)

    @property
    def providers(self):
        """List providers"""
        return self.manager.providers

    @property
    def private_key(self):
        """Get a private key"""
        return self._private_key

    @private_key.setter
    def private_key(self, value: str) -> None:
        """Set a private key used with the TronAPI instance,
        used for obtaining the address, signing transactions etc...

        Args:
            value (str): Private key
        """
        try:
            private_key = PrivateKey(value)
        except ValueError:
            raise TronError('Invalid private key provided')

        self._private_key = str(private_key).lower()

    @property
    def default_address(self) -> AttributeDict:
        """Get a TRON Address"""
        return self._default_address

    @default_address.setter
    def default_address(self, address: str) -> None:
        """Sets the address used with all Tron API.
        Will not sign any transactions.

        Args:
             address (str) Tron Address

        """

        if not self.isAddress(address):
            raise InvalidTronError('Invalid address provided')

        _hex = self.address.to_hex(address)
        _base58 = self.address.from_hex(address)
        _private_base58 = self.address.from_private_key(self._private_key).base58

        # check the addresses
        if self._private_key and _private_base58 != _base58:
            self._private_key = None

        self._default_address = AttributeDict({
            'hex': _hex,
            'base58': _base58
        })

    def get_event_result(self, **kwargs):
        """Will return all events matching the filters.

        Args:
            kwargs (any): List parameters
        """

        # Check the most necessary parameters
        since_timestamp = kwargs.setdefault('since_timestamp', 0)
        event_name = kwargs.setdefault('event_name', 'Notify')
        block_number = kwargs.setdefault('block_number', '')
        size = kwargs.setdefault('size', 20)
        page = kwargs.setdefault('page', 1)
        only_confirmed = kwargs.setdefault('only_confirmed', None)
        only_unconfirmed = kwargs.setdefault('only_unconfirmed', None)
        previous_last = kwargs.setdefault('previous_last_event_fingerprint', None)
        contract_address = kwargs.setdefault('contract_address', self.default_address.hex)

        if not self.isAddress(contract_address):
            raise InvalidTronError('Invalid contract address provided')

        if event_name and not contract_address:
            raise TronError('Usage of event name filtering requires a contract address')

        if block_number and event_name is None:
            raise TronError('Usage of block number filtering requires an event name')

        if not is_integer(page):
            raise ValueError('Invalid size provided')

        if not is_integer(since_timestamp):
            raise ValueError('Invalid sinceTimestamp provided')

        # If the size exceeds 200, displays an error
        if size > 200:
            raise ValueError('Defaulting to maximum accepted size: 200')

        # We collect all parameters in one array
        route_params = []
        if contract_address:
            route_params.append(contract_address)
        if event_name:
            route_params.append(event_name)
        if block_number:
            route_params.append(block_number)

        route = '/'.join(route_params)

        qs = {
            'since': since_timestamp,
            'page': page,
            'size': size
        }

        if only_confirmed is not None:
            qs.update({'onlyConfirmed': only_confirmed})

        if only_unconfirmed is not None and not only_confirmed:
            qs.update({'onlyUnconfirmed': only_unconfirmed})

        if previous_last is not None:
            qs.update({'previousLastEventFingerprint': previous_last})

        return self.manager.request("/event/contract/{0}?{1}"
                                    .format(route, urlencode(qs)), method='get')

    def get_event_transaction_id(self, tx_id):
        """Will return all events within a transactionID.

        Args:
            tx_id (str): TransactionID to query for events.
        """
        response = self.manager.request('/event/transaction/' + tx_id, method='get')
        return response

    @property
    def address(self) -> Address:
        """Helper object that allows you to convert
        between hex/base58 and private key representations of a TRON address.

        Note:
            If you wish to convert generic data to hexadecimal strings,
            please use the function tron.to_hex.

        """
        return Address()

    @property
    def create_account(self) -> PrivateKey:
        """Create account

        Warning: Please control risks when using this API.
        To ensure environmental security, please do not invoke APIs
        provided by other or invoke this very API on a public network.

        """
        return Account.create()

    @staticmethod
    def is_valid_provider(provider) -> bool:
        """Check connected provider

        Args:
            provider(HttpProvider): Provider
        """
        return isinstance(provider, HttpProvider)

    def solidity_sha3(self, abi_types, values):
        """
            Executes keccak256 exactly as Solidity does.
            Takes list of abi_types as inputs -- `[uint24, int8[], bool]`
            and list of corresponding values  -- `[20, [-1, 5, 0], True]`

            Args:
                abi_types (any): types abi
                values (any): values

            Examples:
                >>> tron = Tron()
                >>> sol = tron.solidity_sha3(['uint8[]'], [[1, 2, 3, 4, 5]])
                >>> assert sol.hex() == '0x5917e5a395fb9b454434de59651d36822a9e29c5ec57474df3e67937b969460c'

        """
        if len(abi_types) != len(values):
            raise ValueError(
                "Length mismatch between provided abi types and values.  Got "
                "{0} types and {1} values.".format(len(abi_types), len(values))
            )

        normalized_values = map_abi_data([abi_resolver()], abi_types, values)

        hex_string = add_0x_prefix(''.join(
            remove_0x_prefix(hex_encode_abi_type(abi_type, value))
            for abi_type, value
            in zip(abi_types, normalized_values)
        ))
        return self.keccak(hexstr=hex_string)

    @staticmethod
    @apply_to_return_value(HexBytes)
    def keccak(primitive=None, text=None, hexstr=None):
        if isinstance(primitive, (bytes, int, type(None))):
            input_bytes = to_bytes(primitive, hexstr=hexstr, text=text)
            return tron_keccak(input_bytes)

        raise TypeError(
            "You called keccak with first arg %r and keywords %r. You must call it with one of "
            "these approaches: keccak(text='txt'), keccak(hexstr='0x747874'), "
            "keccak(b'\\x74\\x78\\x74'), or keccak(0x747874)." % (
                primitive,
                {'text': text, 'hexstr': hexstr}
            )
        )

    def is_connected(self):
        """List of available providers"""
        return self.manager.is_connected()


================================================
FILE: tronapi/manager.py
================================================
# --------------------------------------------------------------------
# Copyright (c) iEXBase. All rights reserved.
# Licensed under the MIT License.
# See License.txt in the project root for license information.
# --------------------------------------------------------------------

"""
    tronapi.manager
    ===============

    This class is designed to configure and
    define nodes for different types.

    :copyright: © 2019 by the iEXBase.
    :license: MIT License
"""
from trx_utils import is_string

from tronapi import HttpProvider
from tronapi.constants import DEFAULT_NODES

# In this variable, you can specify the base paths
# to test the connection with the nodes.
# It is advisable to leave the settings unchanged.
STATUS_PAGE = {
    'full_node': '/wallet/getnowblock',
    'solidity_node': '/walletsolidity/getnowblock',
    'event_server': '/healthcheck'
}


class TronManager(object):
    """This class is designed to configure and define nodes
    for different types.

    """

    _providers = None

    def __init__(self, tron, providers):
        """Create new manager tron instance

        Args:
            tron: The tron implementation
            providers: List of providers

        """
        self.tron = tron
        self.providers = providers
        self.preferred_node = None

        for key, value in self.providers.items():
            # This condition checks the nodes,
            # if the link to the node is not specified,
            # we insert the default value to avoid an error.
            if not providers[key]:
                self.providers[key] = HttpProvider(DEFAULT_NODES[key])

            # If the type of the accepted provider is lower-case,
            # then we transform it to “HttpProvider”,
            if is_string(value):
                self.providers[key] = HttpProvider(value)
            self.providers[key].status_page = STATUS_PAGE[key]

    @property
    def providers(self):
        """Getting a list of all providers

        """
        return self._providers or tuple()

    @providers.setter
    def providers(self, value) -> None:
        """Add a new provider

        """
        self._providers = value

    @property
    def full_node(self) -> HttpProvider:
        """Getting and managing paths to a full node

        """
        if 'full_node' not in self.providers:
            raise ValueError('Full node is not activated.')
        return self.providers.get('full_node')

    @property
    def solidity_node(self) -> HttpProvider:
        """Getting and managing paths to a solidity node

        """
        if 'solidity_node' not in self.providers:
            raise ValueError('Solidity node is not activated.')
        return self.providers.get('solidity_node')

    @property
    def event_server(self) -> HttpProvider:
        """Getting and managing paths to a event server

        """
        if 'event_server' not in self.providers:
            raise ValueError('Event server is not activated.')
        return self.providers.get('event_server')

    def request(self, url, params=None, method=None):
        """Prepare and route the request object according to the manager's configuration.

        Args:
            url (str): Path to send
            params (dict): Options
            method (str): Request method

        """
        method = 'post' if method is None else method

        # In this variable, we divide the resulting reference
        # into 2 parts to determine the type of node
        split = url[1:].split('/', 2)

        if split[0] in ('walletsolidity', 'walletextension',):
            return self.solidity_node.request(url, json=params, method=method)
        elif split[0] in ('wallet',):
            return self.full_node.request(url, json=params, method=method)
        elif split[0] in ('event', 'healthcheck',):
            return self.event_server.request(url, json=params, method=method)

        raise ValueError('Could not determine the type of node')

    def is_connected(self):
        """Check connection with providers"""
        is_node = dict()
        for key, value in self.providers.items():
            is_node.update({key: value.is_connected()})
        return is_node


================================================
FILE: tronapi/module.py
================================================
# --------------------------------------------------------------------
# Copyright (c) iEXBase. All rights reserved.
# Licensed under the MIT License.
# See License.txt in the project root for license information.
# --------------------------------------------------------------------


class Module:
    """Module Class"""

    def __init__(self, tron) -> None:
        self.tron = tron

    @classmethod
    def attach(cls, target, module_name: str = None) -> None:
        if not module_name:
            module_name = cls.__name__.lower()

        if hasattr(target, module_name):
            raise AttributeError(
                "Cannot set {0} module named '{1}'.  The Tron object "
                "already has an attribute with that name".format(
                    target,
                    module_name,
                )
            )

        if isinstance(target, Module):
            tron = target.tron
        else:
            tron = target

        setattr(target, module_name, cls(tron))


================================================
FILE: tronapi/providers/__init__.py
================================================


================================================
FILE: tronapi/providers/base.py
================================================
# --------------------------------------------------------------------
# Copyright (c) iEXBase. All rights reserved.
# Licensed under the MIT License.
# See License.txt in the project root for license information.
# --------------------------------------------------------------------
import platform

import tronapi


class BaseProvider(object):
    _status_page = None

    @property
    def status_page(self):
        """Get the page to check the connection"""
        return self._status_page

    @status_page.setter
    def status_page(self, page):
        self._status_page = page

    @staticmethod
    def _http_default_headers():
        """Add default headers"""
        return {
            'Content-Type': 'application/json',
            'User-Agent': BaseProvider.format_user_agent()
        }

    @staticmethod
    def format_user_agent(name=None):
        """Construct a User-Agent suitable for use in client code.
        This will identify use by the provided ``name`` (which should be on the
        format ``dist_name/version``), TronAPI version and Python version.
        .. versionadded:: 1.1
        """
        parts = ['TronAPI/%s' % tronapi.__version__,
                 '%s/%s' % (platform.python_implementation(),
                            platform.python_version())]
        if name:
            parts.insert(0, name)
        return ' '.join(parts)


================================================
FILE: tronapi/providers/http.py
================================================
# --------------------------------------------------------------------
# Copyright (c) iEXBase. All rights reserved.
# Licensed under the MIT License.
# See License.txt in the project root for license information.
# --------------------------------------------------------------------


"""
    tronapi.providers.http
    ======================

    Class for configuring http providers

    :copyright: © 2018 by the iEXBase.
    :license: MIT License
"""
import logging
from collections import namedtuple
from urllib.parse import urlparse

from eth_utils import to_dict
from requests import Session
from requests.exceptions import (
    ConnectionError as TrxConnectionError
)

from tronapi.common.encoding import to_text
from tronapi.providers.base import BaseProvider
from tronapi.exceptions import HTTP_EXCEPTIONS, TransportError

HTTP_SCHEMES = {'http', 'https'}
HttpResponse = namedtuple('HttpResponse', ('status_code', 'headers', 'data'))

log = logging.getLogger(__name__)


class HttpProvider(BaseProvider):
    """A Connection object to make HTTP requests to a particular node."""

    def __init__(self, node_url, request_kwargs=None):
        """Initializes a :class:`~tronapi.providers.http.HttpProvider`
        instance.

         Args:
            node_url (str):  Url of the node to connect to.
            request_kwargs (dict): Optional params to send with each request.

        """

        self.node_url = node_url.rstrip('/')
        uri = urlparse(node_url)
        # This condition checks the node that will connect
        # to work with methods.
        if uri.scheme not in HTTP_SCHEMES:
            raise NotImplementedError(
                'TronAPI does not know how to connect to scheme %r in %r' % (
                    uri.scheme,
                    self.node_url,
                )
            )

        self._request_kwargs = request_kwargs or {}
        self.session = Session()

    @to_dict
    def get_request_kwargs(self):
        """Header settings"""
        if 'headers' not in self._request_kwargs:
            yield 'headers', self._http_default_headers()
        for key, value in self._request_kwargs.items():
            yield key, value

    def request(self, path, json=None, params=None, method=None):
        """Performs an HTTP request with the given parameters.

           Args:
               path (str): API endpoint path (e.g.: ``'/transactions'``).
               json (dict): JSON data to send along with the request.
               params (dict): Dictionary of URL (query) parameters.
               method (str): HTTP method (e.g.: ``'GET'``).

        """
        try:
            response = self._request(
                method=method,
                url=self.node_url + path if path else self.node_url,
                json=json,
                params=params,
                **self.get_request_kwargs(),
            )
        except TrxConnectionError as err:
            raise err

        return response.data

    def is_connected(self) -> bool:
        """Connection check

        This method sends a test request to the connected node
        to determine its health.

        Returns:
            bool: True if successful,
            False otherwise.
        """
        response = self.request(path=self.status_page, method='get')
        if 'blockID' in response or response == 'OK':
            return True

        return False

    def _request(self, **kwargs):

        kwargs.setdefault('timeout', 60)

        response = self.session.request(**kwargs)
        text = response.text

        try:
            json = response.json()
        except ValueError:
            json = None

        if not (200 <= response.status_code < 300):
            exc_cls = HTTP_EXCEPTIONS.get(response.status_code, TransportError)
            raise exc_cls(response.status_code, text, json, kwargs.get('url'))

        data = json if json is not None else text
        log.debug(data)

        # Additional error interceptor that will occur in case of failed requests
        if 'Error' in data:
            raise ValueError(data['Error'])

        self.__error_manager(data)

        return HttpResponse(response.status_code, response.headers, data)

    @staticmethod
    def __error_manager(data):
        """Manager error

        Args:
            data (any): response data

        """
        # Additional error interceptor that will occur in case of failed requests
        if 'Error' in data:
            raise ValueError(data['Error'])

        # Convert hash errors
        if 'code' in data and 'message' in data:
            data['message'] = to_text(hexstr=data['message'])


================================================
FILE: tronapi/transactionbuilder.py
================================================
# --------------------------------------------------------------------
# Copyright (c) iEXBase. All rights reserved.
# Licensed under the MIT License.
# See License.txt in the project root for license information.
# --------------------------------------------------------------------

from datetime import datetime
from typing import (
    Any,
    Tuple,
    List
)

from eth_abi import encode_abi
from trx_utils import (
    is_string,
    is_integer,
    is_boolean,
    is_hex,
    encode_hex
)

from tronapi.exceptions import (
    InvalidTronError,
    TronError,
    InvalidAddress
)
from tronapi.common.validation import is_valid_url

DEFAULT_TIME = datetime.now()
START_DATE = int(DEFAULT_TIME.timestamp() * 1000)


class TransactionBuilder(object):
    def __init__(self, tron):
        self.tron = tron

    def send_transaction(self, to, amount, account=None):
        """Creates a transaction of transfer.
        If the recipient address does not exist, a corresponding account will be created.

        Args:
            to (str): to address
            amount (float): amount
            account (str): from address

        Returns:
            Transaction contract data

        """

        # If the address of the sender is not specified, we prescribe the default
        if account is None:
            account = self.tron.default_address.hex

        if not self.tron.isAddress(to):
            raise InvalidTronError('Invalid recipient address provided')

        if not isinstance(amount, float) or amount <= 0:
            raise InvalidTronError('Invalid amount provided')

        _to = self.tron.address.to_hex(to)
        _from = self.tron.address.to_hex(account)

        if _to == _from:
            raise TronError('Cannot transfer TRX to the same account')

        response = self.tron.manager.request('/wallet/createtransaction', {
            'to_address': _to,
            'owner_address': _from,
            'amount': self.tron.toSun(amount)
        })

        return response

    def send_token(self, to, amount, token_id, account=None):
        """Transfer Token

        Args:
            to (str): is the recipient address
            amount (int): is the amount of token to transfer. must be integer instead of float
            token_id (any): Token Name and id
            account: (str): is the address of the withdrawal account

        Returns:
            Token transfer Transaction raw data

        """

        # If the address of the sender is not specified, we prescribe the default
        if account is None:
            account = self.tron.default_address.hex

        if not self.tron.isAddress(to):
            raise InvalidTronError('Invalid recipient address provided')

        if not isinstance(amount, int) or amount <= 0:
            raise InvalidTronError('Invalid amount provided')

        if not token_id:
            raise InvalidTronError('Invalid token ID provided')

        if not self.tron.isAddress(account):
            raise InvalidTronError('Invalid origin address provided')

        _to = self.tron.address.to_hex(to)
        _from = self.tron.address.to_hex(account)
        _token_id = self.tron.toHex(text=str(token_id))

        if _to == _from:
            raise TronError('Cannot transfer TRX to the same account')

        # In case if "TRX" is specified, we redirect to another method.
        if is_string(token_id) and token_id.upper() == 'TRX':
            return self.send_transaction(_to, amount, _from)

        return self.tron.manager.request('/wallet/transferasset', {
            'to_address': _to,
            'owner_address': _from,
            'asset_name': _token_id,
            'amount': amount
        })

    def freeze_balance(self, amount, duration, resource, account=None):
        """
        Freezes an amount of TRX.
        Will give bandwidth OR Energy and TRON Power(voting rights)
        to the owner of the frozen tokens.

        Args:
            amount (int): number of frozen trx
            duration (int): duration in days to be frozen
            resource (str): type of resource, must be either "ENERGY" or "BANDWIDTH"
            account (str): address that is freezing trx account

        """

        # If the address of the sender is not specified, we prescribe the default
        if account is None:
            account = self.tron.default_address.hex

        if resource not in ('BANDWIDTH', 'ENERGY',):
            raise InvalidTronError('Invalid resource provided: Expected "BANDWIDTH" or "ENERGY"')

        if not is_integer(amount) or amount <= 0:
            raise InvalidTronError('Invalid amount provided')

        if not is_integer(duration) or duration < 3:
            raise InvalidTronError('Invalid duration provided, minimum of 3 days')

        if not self.tron.isAddress(account):
            raise InvalidTronError('Invalid address provided')

        response = self.tron.manager.request('/wallet/freezebalance', {
            'owner_address': self.tron.address.to_hex(account),
            'frozen_balance': self.tron.toSun(amount),
            'frozen_duration': int(duration),
            'resource': resource
        })

        if 'Error' in response:
            raise TronError(response['Error'])

        return response

    def unfreeze_balance(self, resource='BANDWIDTH', account=None):
        """
        Unfreeze TRX that has passed the minimum freeze duration.
        Unfreezing will remove bandwidth and TRON Power.

        Args:
            resource (str): type of resource, must be either "ENERGY" or "BANDWIDTH"
            account (str): address that is freezing trx account

        """

        # If the address of the sender is not specified, we prescribe the default
        if account is None:
            account = self.tron.default_address.hex

        if resource not in ('BANDWIDTH', 'ENERGY',):
            raise InvalidTronError('Invalid resource provided: Expected "BANDWIDTH" or "ENERGY"')

        if not self.tron.isAddress(account):
            raise InvalidTronError('Invalid address provided')

        response = self.tron.manager.request('/wallet/unfreezebalance', {
            'owner_address': self.tron.address.to_hex(account),
            'resource': resource
        })

        if 'Error' in response:
            raise ValueError(response['Error'])

        return response

    def purchase_token(self, to: str, token_id: str, amount: int, buyer=None):
        """Purchase a Token
        Creates an unsigned ICO token purchase transaction.

        Args:
            to (str): is the address of the Token issuer
            token_id (str): is the name of the token
            amount (int): is the number of tokens created
            buyer (str): is the address of the Token owner

        """

        if buyer is None:
            buyer = self.tron.default_address.hex

        if not self.tron.isAddress(to):
            raise InvalidAddress('Invalid to address provided')

        if not len(token_id):
            raise ValueError('Invalid token ID provided')

        if amount <= 0:
            raise ValueError('Invalid amount provided')

        _to = self.tron.address.to_hex(to)
        _from = self.tron.address.to_hex(buyer)

        return self.tron.manager.request('/wallet/participateassetissue', {
            'to_address': _to,
            'owner_address': _from,
            'asset_name': self.tron.toHex(text=token_id),
            'amount': int(amount)
        })

    def withdraw_block_rewards(self, address: str = None):
        """Withdraw block rewards
        Creates an unsigned Super Representative award balance withdraw transaction.

        Args:
            address (str): Optional address to withdraw from.

        """
        if not address:
            address = self.tron.default_address.hex

        if not self.tron.isAddress(address):
            raise InvalidAddress('Invalid address provided')

        return self.tron.manager.request('/wallet/withdrawbalance', {
            'owner_address': self.tron.address.to_hex(address)
        })

    def apply_for_sr(self, url, address=None):
        """Apply to become a super representative

        Args:
            url (str): official website address
            address (str): address

        """

        # If the address of the sender is not specified, we prescribe the default
        if address is None:
            address = self.tron.default_address.hex

        if not self.tron.isAddress(address):
            raise TronError('Invalid address provided')

        if not is_valid_url(url):
            raise TronError('Invalid url provided')

        return self.tron.manager.request('/wallet/createwitness', {
            'owner_address': self.tron.address.to_hex(address),
            'url': self.tron.toHex(text=url)
        })

    def vote(self, votes: List[Tuple[str, int]], voter_address: str = None):
        """Vote
        Vote on the super representative

        Args:
            votes (dict): dictionary of SR address : vote count key-value pair
            voter_address: voter address

        Examples:
            >>> from tronapi import Tron
            >>> data = [
            >>>     ('TRJpw2uqohP7FUmAEJgt57wakRn6aGQU6Z', 1)
            >>> ]
            >>> tron = Tron()
            >>> tron.transaction.vote(data)

        """
        if voter_address is None:
            voter_address = self.tron.default_address.hex

        _view_vote = []

        # We create a cycle to check all the received data for voting.
        for sr_address, vote_count in votes:
            if not self.tron.isAddress(sr_address):
                raise InvalidAddress(
                    'Invalid SR address provided: ' + sr_address
                )

            if not is_integer(vote_count) or vote_count <= 0:
                raise ValueError(
                    'Invalid vote count provided for SR: ' + sr_address
                )

            _view_vote.append({
                'vote_address': self.tron.address.to_hex(sr_address),
                'vote_count': int(vote_count)
            })

        return self.tron.manager.request('/wallet/votewitnessaccount', {
            'owner_address': self.tron.address.to_hex(voter_address),
            'votes': _view_vote
        })

    def create_proposal(self, parameters: Any, issuer_address=None):
        """Creates a proposal to modify the network.
        Can only be created by a current Super Representative.

        Args:
            parameters (Any): proposal parameters
            issuer_address: owner address

        Examples:
            >>> from tronapi import Tron
            >>> data = [
            >>>     {'key': 1, 'value': 2},
            >>>     {'key': 1, 'value': 2}
            >>> ]
            >>> tron = Tron()
            >>> tron.transaction.create_proposal(data)


        """
        if issuer_address is None:
            issuer_address = self.tron.default_address.hex

        if not self.tron.isAddress(issuer_address):
            raise InvalidAddress('Invalid issuerAddress provided')

        return self.tron.manager.request('/wallet/proposalcreate', {
            'owner_address': self.tron.address.to_hex(issuer_address),
            'parameters': parameters
        })

    def vote_proposal(self, proposal_id, has_approval, voter_address=None):
        """Proposal approval

        Args:
            proposal_id (int): proposal id
            has_approval (bool): Approved
            voter_address (str): Approve address

        """

        # If the address of the sender is not specified, we prescribe the default
        if voter_address is None:
            voter_address = self.tron.default_address.hex

        if not self.tron.isAddress(voter_address):
            raise TronError('Invalid voter_address address provided')

        if not is_integer(proposal_id) or proposal_id < 0:
            raise TronError('Invalid proposal_id provided')

        if not is_boolean(has_approval):
            raise TronError('Invalid has_approval provided')

        return self.tron.manager.request('/wallet/proposalapprove', {
            'owner_address': self.tron.address.to_hex(voter_address),
            'proposal_id': int(proposal_id),
            'is_add_approval': bool(has_approval)
        })

    def delete_proposal(self, proposal_id: int, issuer_address: str = None):
        """Delete proposal

        Args:
            proposal_id (int): proposal id
            issuer_address (str): delete the person's address

        Results:
            Delete the proposal's transaction

        """

        # If the address of the sender is not specified, we prescribe the default
        if issuer_address is None:
            issuer_address = self.tron.default_address.hex

        if not self.tron.isAddress(issuer_address):
            raise InvalidTronError('Invalid issuer_address provided')

        if not isinstance(proposal_id, int) or proposal_id < 0:
            raise InvalidTronError('Invalid proposal_id provided')

        return self.tron.manager.request('/wallet/proposaldelete', {
            'owner_address': self.tron.address.to_hex(issuer_address),
            'proposal_id': int(proposal_id)
        })

    def update_account(self, account_name, account: str = None):
        """Modify account name

        Note: Username is allowed to edit only once.

        Args:
            account_name (str): name of the account
            account (str): address

        Returns:
            modified Transaction Object

        """

        # If the address of the sender is not specified, we prescribe the default
        if account is None:
            account = self.tron.default_address.hex

        if not is_string(account_name):
            raise ValueError('Name must be a string')

        if not self.tron.isAddress(account):
            raise TronError('Invalid origin address provided')

        response = self.tron.manager.request('/wallet/updateaccount', {
            'account_name': self.tron.toHex(text=account_name),
            'owner_address': self.tron.address.to_hex(account)
        })

        return response

    def create_smart_contract(self, **kwargs):
        """Deploy Contract

        Deploys a contract.
        Returns TransactionExtention, which contains an unsigned transaction.

        Example:
        .. code-block:: python
            >>> from tronapi import Tron
            >>>
            >>> tron = Tron()
            >>> tron.transaction_builder.create_smart_contract(
            >>>    fee_limit=10**9,
            >>>    call_value=0,
            >>>    consume_user_resource_percent=10
            >>> )

        Args:
            **kwargs: Transaction parameters for the deployment
            transaction as a dict

        """

        if 'bytecode' not in kwargs:
            raise ValueError(
                "Cannot deploy a contract that does not have 'bytecode' associated "
                "with it"
            )

        # Maximum TRX consumption, measured in SUN (1 TRX = 1,000,000 SUN).
        fee_limit = kwargs.setdefault('fee_limit', 0)
        # The same as User Pay Ratio.
        # The percentage of resources specified for users who use this contract.
        # This field accepts integers between [0, 100].
        user_fee_percentage = kwargs.setdefault('consume_user_resource_percent', 0)
        # Amount of TRX transferred with this transaction, measured in SUN (1TRX = 1,000,000 SUN)
        call_value = kwargs.setdefault('call_value', 0)
        # Contract owner address, converted to a hex string
        owner_address = kwargs.setdefault('owner_address', self.tron.default_address.hex)
        # The max energy which will be consumed by the owner
        # in the process of excution or creation of the contract,
        # is an integer which should be greater than 0.
        origin_energy_limit = kwargs.setdefault('origin_energy_limit', 10000000)

        if not is_integer(user_fee_percentage) and not user_fee_percentage:
            user_fee_percentage = 100

        if not is_hex(kwargs.get('bytecode')):
            raise ValueError('Invalid bytecode provided')

        if not is_integer(fee_limit) or fee_limit <= 0 or \
                fee_limit > 1000000000:
            raise ValueError('Invalid fee limit provided')

        if not is_integer(call_value) or call_value < 0:
            raise ValueError('Invalid call value provided')

        if not is_integer(user_fee_percentage) or user_fee_percentage < 0 or \
                user_fee_percentage > 100:
            raise ValueError('Invalid user fee percentage provided')

        if not is_integer(origin_energy_limit) or origin_energy_limit < 0:
            return ValueError('Invalid origin_energy_limit provided')

        if not self.tron.isAddress(owner_address):
            raise InvalidAddress('Invalid issuer address provided')

        # We write all the results in one object
        transaction = dict(**kwargs)
        transaction.setdefault('owner_address', self.tron.address.to_hex(owner_address))

        return self.tron.manager.request('/wallet/deploycontract',
                                         transaction)

    def trigger_smart_contract(self, **kwargs):
        """Trigger Smart Contract
        Calls a function on a contract

        Args:
            **kwargs: Fill in the required parameters

        Examples:
            >>> tron = Tron()
            >>> tron.transaction_builder.trigger_smart_contract(
            >>>     contract_address='413c8143e98b3e2fe1b1a8fb82b34557505a752390',
            >>>     function_selector='set(uint256,uint256)',
            >>>     fee_limit=30000,
            >>>     call_value=0,
            >>>     parameters=[
            >>>        {'type': 'int256', 'value': 1},
            >>>        {'type': 'int256', 'value': 1}
    ]
)

        Returns:
            TransactionExtention, TransactionExtention contains unsigned Transaction
        """

        contract_address = kwargs.setdefault('contract_address', None)
        function_selector = kwargs.setdefault('function_selector', None)
        parameters = kwargs.setdefault('parameters', [])
        issuer_address = kwargs.setdefault('issuer_address', self.tron.default_address.hex)
        call_value = kwargs.setdefault('call_value', 0)
        fee_limit = kwargs.setdefault('fee_limit', 1000000000)
        token_value = kwargs.setdefault('token_value', 0)
        token_id = kwargs.setdefault('token_id', 0)

        if not is_integer(token_value) or token_value < 0:
            raise ValueError('Invalid options.tokenValue provided')

        if not is_integer(token_id) or token_id < 0:
            raise ValueError('Invalid options.tokenId provided')

        if not self.tron.isAddress(contract_address):
            raise InvalidAddress('Invalid contract address provided')

        if not is_string(function_selector):
            raise ValueError('Invalid function selector provided')

        if not is_integer(call_value) or call_value < 0:
            raise ValueError('Invalid call value provided')

        if not is_integer(fee_limit) or fee_limit <= 0 or fee_limit > 1000000000:
            raise ValueError('Invalid fee limit provided')

        function_selector = function_selector.replace('/\s*/g', '')

        if len(parameters) > 0:
            types = []
            values = []
            for abi in parameters:
                if 'type' not in abi or not is_string(abi['type']):
                    raise ValueError('Invalid parameter type provided: ' + abi['type'])

                if abi['type'] == 'address':
                    abi['value'] = self.tron.address.to_hex(abi['value']).replace('41', '0x', 1)

                types.append(abi['type'])
                values.append(abi['value'])

            try:
                parameters = encode_hex(encode_abi(types, values)).replace('0x', '', 2)
            except ValueError as ex:
                print(ex)

        else:
            parameters = ''

        data = {
            'contract_address': self.tron.address.to_hex(contract_address),
            'owner_address': self.tron.address.to_hex(issuer_address),
            'function_selector': function_selector,
            'fee_limit': int(fee_limit),
            'call_value': int(call_value),
            'parameter': parameters
        }

        if token_value:
            data['call_token_value'] = int(token_value)

        if token_id:
            data['token_id'] = int(token_id)

        return self.tron.manager.request('/wallet/triggersmartcontract', data)

    def create_trx_exchange(self,
                            token_name: str,
                            token_balance: int,
                            trx_balance: int,
                            account: str = None):
        """Create an exchange between a token and TRX.
        Token Name should be a CASE SENSITIVE string.
        Note: PLEASE VERIFY THIS ON TRONSCAN.

        Args:
            token_name (str): Token Name
            token_balance (int): balance of the first token
            trx_balance (int): balance of the second token
            account (str): Owner Address
        """

        # If the address of the sender is not specified, we prescribe the default
        if account is None:
            account = self.tron.default_address.hex

        if not self.tron.isAddress(account):
            raise TronError('Invalid address provided')

        if token_balance <= 0 or trx_balance <= 0:
            raise TronError('Invalid amount provided')

        return self.tron.manager.request('/wallet/exchangecreate', {
            'owner_address': self.tron.address.to_hex(account),
            'first_token_id': self.tron.toHex(text=token_name),
            'first_token_balance': token_balance,
            'second_token_id': '5f',
            'second_token_balance': trx_balance
        })

    def create_token_exchange(self,
                              first_token_name: str,
                              first_token_balance: int,
                              second_token_name: str,
                              second_token_balance: int,
                              owner_address: str = None):
        """Create an exchange between a token and another token.
        DO NOT USE THIS FOR TRX.
        Token Names should be a CASE SENSITIVE string.

        Args:
            first_token_name (str): the id of the first token
            first_token_balance (int): balance of the first token
            second_token_name (str): the id of the second token
            second_token_balance (int): balance of the second token
            owner_address: owner address

        """
        if owner_address is None:
            owner_address = self.tron.default_address.hex

        if not self.tron.isAddress(owner_address):
            raise InvalidAddress('Invalid address provided')

        if second_token_balance <= 0 or first_token_balance <= 0:
            raise ValueError('Invalid amount provided')

        return self.tron.manager.request('/wallet/exchangecreate', {
            'owner_address': self.tron.address.to_hex(owner_address),
            'first_token_id': self.tron.toHex(text=first_token_name),
            'first_token_balance': first_token_balance,
            'second_token_id': self.tron.toHex(text=second_token_name),
            'second_token_balance': second_token_balance
        })

    def inject_exchange_tokens(self,
                               exchange_id: int,
                               token_name: str,
                               token_amount: int = 0,
                               owner_address: str = None):
        """Adds tokens into a bancor style exchange.
        Will add both tokens at market rate.

        Args:
            exchange_id (int): non-negative integer exchange id
            token_name (str): token name
            token_amount (int): amount of token
            owner_address (str): token owner address in hex

        """
        if owner_address is None:
            owner_address = self.tron.default_address.hex

        if not self.tron.isAddress(owner_address):
            raise InvalidAddress('Invalid owner_address provided')

        if exchange_id < 0:
            raise ValueError('Invalid exchange_id provided')

        if token_amount < 1:
            raise ValueError('Invalid token_amount provided')

        return self.tron.manager.request('/wallet/exchangeinject', {
            'owner_address': self.tron.address.to_hex(owner_address),
            'exchange_id': exchange_id,
            'token_id': self.tron.toHex(text=token_name),
            'quant': token_amount
        })

    def create_token(self, **kwargs):
        """Issue Token

        Issuing a token on the TRON Protocol can be done by anyone
        who has at least 1024 TRX in their account.
        When a token is issued it will be shown on the token overview page.
        Users can then participate within the issuing time and exchange their
        TRX for tokens.After issuing the token your account will
        receive the amount of tokens equal to the total supply.
        When other users exchange their TRX for tokens then the tokens
        will be withdrawn from your account and you will receive
        TRX equal to the specified exchange rate.


        Args:
            **kwargs: Fill in the required parameters

        Examples:

            >>> start_func = datetime.now()
            >>> start = int(start_func.timestamp() * 1000)
            >>>
            >>> end_func = datetime.now() + timedelta(days=2)
            >>> end = int(end_func.timestamp() * 1000)
            >>>
            >>> opt = {
            >>>     'name': 'Tron',
            >>>     'abbreviation': 'TRX',
            >>>     'description': 'Hello World',
            >>>     'url': 'https://github.com',
            >>>     'totalSupply': 25000000,
            >>>     'frozenAmount': 1,
            >>>     'frozenDuration': 2,
            >>>     'freeBandwidth': 10000,
            >>>     'freeBandwidthLimit': 10000,
            >>>     'saleStart': start,
            >>>     'saleEnd': end,
            >>>     'voteScore': 1
            >>> }

        """
        issuer_address = kwargs.setdefault(
            'issuer_address', self.tron.default_address.hex
        )

        if not self.tron.isAddress(issuer_address):
            raise TronError('Invalid issuer address provided')

        total_supply = kwargs.setdefault('totalSupply', 0)
        trx_ratio = kwargs.setdefault('trxRatio', 1)
        token_ratio = kwargs.setdefault('tokenRatio', 1)
        sale_start = kwargs.setdefault(
            'saleStart', START_DATE
        )
        free_bandwidth = kwargs.setdefault('freeBandwidth', 0)
        free_bandwidth_limit = kwargs.setdefault('freeBandwidthLimit', 0)
        frozen_amount = kwargs.setdefault('frozenAmount', 0)
        frozen_duration = kwargs.setdefault('frozenDuration', 0)
        vote_score = kwargs.setdefault('voteScore', 0)
        precision = kwargs.setdefault('precision', 0)

        if not is_string(kwargs.get('name')):
            raise ValueError('Invalid token name provided')

        if not is_string(kwargs.get('abbreviation')):
            raise ValueError('Invalid token abbreviation provided')

        if not is_integer(total_supply) or total_supply <= 0:
            raise ValueError('Invalid supply amount provided')

        if not is_integer(trx_ratio) or trx_ratio <= 0:
            raise ValueError('TRX ratio must be a positive integer')

        if not is_integer(token_ratio) or token_ratio <= 0:
            raise ValueError('Token ratio must be a positive integer')

        if not is_integer(vote_score) or vote_score <= 0:
            raise ValueError('voteScore must be a positive integer greater than 0')

        if not is_integer(precision) or precision <= 0 or precision > 6:
            raise ValueError('precision must be a positive integer > 0 and <= 6')

        if not is_integer(sale_start) or sale_start < START_DATE:
            raise ValueError('Invalid sale start timestamp provided')

        if not is_integer(kwargs.get('saleEnd')) or \
                kwargs.get('saleEnd') <= sale_start:
            raise ValueError('Invalid sale end timestamp provided')

        if not is_string(kwargs.get('description')):
            raise ValueError('Invalid token description provided')

        if not is_valid_url(kwargs.get('url')):
            raise ValueError('Invalid token url provided')

        if not is_integer(free_bandwidth) or free_bandwidth < 0:
            raise ValueError('Invalid free bandwidth amount provided')

        if not is_integer(free_bandwidth_limit) or free_bandwidth_limit < 0 \
                or (free_bandwidth and not free_bandwidth_limit):
            raise ValueError('Invalid free bandwidth limit provided')

        if not is_integer(frozen_amount) or frozen_amount < 0 \
                or (not frozen_duration and frozen_amount):
            raise ValueError('Invalid frozen supply provided')

        if not is_integer(frozen_duration) or frozen_duration < 0 \
                or (frozen_duration and not frozen_amount):
            raise ValueError('Invalid frozen duration provided')

        frozen_supply = {
            'frozen_amount': int(frozen_amount),
            'frozen_days': int(frozen_duration)
        }

        response = self.tron.manager.request('/wallet/createassetissue', {
            'owner_address': self.tron.address.to_hex(issuer_address),
            'name': self.tron.toHex(text=kwargs.get('name')),
            'abbr': self.tron.toHex(text=kwargs.get('abbreviation')),
            'description': self.tron.toHex(text=kwargs.get('description')),
            'url': self.tron.toHex(text=kwargs.get('url')),
            'total_supply': int(total_supply),
            'trx_num': int(trx_ratio),
            'num': int(token_ratio),
            'start_time': int(sale_start),
            'end_time': int(kwargs.get('saleEnd')),
            'free_asset_net_limit': int(free_bandwidth),
            'public_free_asset_net_limit': int(free_bandwidth_limit),
            'frozen_supply': frozen_supply,
            'vote_score': vote_score,
            'precision': precision
        })

        return response

    def withdraw_exchange_tokens(self,
                                 exchange_id: int,
                                 token_name: str,
                                 token_amount: int = 0,
                                 owner_address: str = None):
        """Withdraws tokens from a bancor style exchange.
        Will withdraw at market rate both tokens.

        Args:
            exchange_id (int): non-negative integer exchange id
            token_name (str): token name
            token_amount (int): number of tokens withdraw
            owner_address (str): owner address in hex

        """
        if owner_address is None:
            owner_address = self.tron.default_address.hex

        if not self.tron.isAddress(owner_address):
            raise InvalidAddress('Invalid owner_address provided')

        if exchange_id < 0:
            raise ValueError('Invalid exchange_id provided')

        if token_amount < 1:
            raise ValueError('Invalid token_amount provided')

        return self.tron.manager.request('/wallet/exchangewithdraw', {
            'owner_address': self.tron.address.to_hex(owner_address),
            'exchange_id': exchange_id,
            'token_id': self.tron.toHex(text=token_name),
            'quant': token_amount
        })

    def trade_exchange_tokens(self,
                              exchange_id: int,
                              token_name: str,
                              token_amount_sold: int = 0,
                              token_amount_expected: int = 0,
                              owner_address: str = None):
        """Trade tokens on a bancor style exchange.
        Expected value is a validation and used to cap the total amt of token 2 spent.

        Args:
            exchange_id (int): non-negative integer exchange id
            token_name (str): token name
            token_amount_sold (int): amount f token actually sold
            token_amount_expected (int): amount of token expected
            owner_address (str): token owner address in hex

        """

        if owner_address is None:
            owner_address = self.tron.default_address.hex

        if not self.tron.isAddress(owner_address):
            raise InvalidAddress('Invalid owner_address provided')

        if exchange_id < 0:
            raise ValueError('Invalid exchange_id provided')

        if token_amount_sold < 1:
            raise ValueError('Invalid token_amount_sold provided')

        if token_amount_expected < 1:
            raise ValueError('Invalid token_amount_expected provided')

        return self.tron.manager.request('/wallet/exchangewithdraw', {
            'owner_address': self.tron.address.to_hex(owner_address),
            'exchange_id': exchange_id,
            'token_id': self.tron.toHex(text=token_name),
            'quant': token_amount_sold,
            'expected': token_amount_expected
        })

    def update_setting(self,
                       contract_address,
                       user_fee_percentage,
                       owner_address: str = None):
        """Update userFeePercentage.

        Args:
            contract_address (str): the address of the contract to be modified
            user_fee_percentage (int): the percentage of resources specified for users using this contract
            owner_address (str): is the address of the creator

        Returns:
            Contains unsigned transaction Transaction
        """

        if owner_address is None:
            owner_address = self.tron.default_address.hex

        if not self.tron.isAddress(owner_address):
            raise InvalidAddress('Invalid owner_address provided')

        if not self.tron.isAddress(contract_address):
            raise InvalidAddress('Invalid contract_address provided')

        if not is_integer(user_fee_percentage) or user_fee_percentage < 0 or \
                user_fee_percentage > 100:
            raise ValueError('Invalid user_fee_percentage provided')

        return self.tron.manager.request('wallet/updatesetting', {
            'owner_address': self.tron.address.to_hex(owner_address),
            'contract_address': self.tron.address.to_hex(contract_address),
            'consume_user_resource_percent': user_fee_percentage
        })

    def update_energy_limit(self,
                            contract_address,
                            origin_energy_limit,
                            owner_address: str = None):
        """Update energy limit.

        Args:
            contract_address (str): The address of the contract to be modified
            origin_energy_limit (int): The maximum energy set by the creator that is created
            owner_address (str): Is the address of the creator

        Returns:
            Contains unsigned transaction Transaction
        """

        if owner_address is None:
            owner_address = self.tron.default_address.hex

        if not self.tron.isAddress(owner_address):
            raise InvalidAddress('Invalid owner_address provided')

        if not self.tron.isAddress(contract_address):
            raise InvalidAddress('Invalid contractAddress provided')

        if not is_integer(origin_energy_limit) or origin_energy_limit < 0 or \
                origin_energy_limit > 10000000:
            raise ValueError('Invalid originEnergyLimit  provided')

        return self.tron.manager.request('wallet/updateenergylimit', {
            'owner_address': self.tron.address.to_hex(owner_address),
            'contract_address': self.tron.address.to_hex(contract_address),
            'origin_energy_limit': origin_energy_limit
        })

    def check_permissions(self, permissions, _type):
        if permissions is not None:
            if permissions['type'] != _type or \
                    not permissions['permission_name'] or \
                    not is_string(permissions['permission_name']) or \
                    not is_integer(permissions['threshold']) or \
                    permissions['threshold'] < 1 or not permissions['keys']:
                return False

        for key in permissions['key']:
            if not self.tron.isAddress(key['address']) or \
                    not is_integer(key['weight']) or \
                    key['weight'] > permissions['threshold'] or \
                    key['weight'] < 1 or _type == 2 and not permissions['operations']:
                return False

        return True

    def update_account_permissions(self, owner_address=None,
                                   owner_permissions=None,
                                   witness_permissions=None,
                                   actives_permissions=None
                                   ):
        """Role: update user permissions (for multi-signature)

        Args:
            owner_address (str): The address of the account whose permissions are to be modified
            owner_permissions: Modified owner permission
            witness_permissions: Modified witness permission (if it is a witness)
            actives_permissions: Modified actives permission
        """

        if owner_address is None:
            owner_address = self.tron.default_address.hex

        if not self.check_permissions(owner_permissions, 0):
            raise InvalidTronError('Invalid ownerPermissions provided')

        if not self.check_permissions(witness_permissions, 1):
            raise InvalidTronError('Invalid witnessPermissions provided')

        for actives_permission in actives_permissions:
            if not self.check_permissions(actives_permission, 2):
                raise InvalidTronError('Invalid activesPermissions provided')

        data = {
            owner_address: owner_address
        }

        if owner_permissions:
            data['owner'] = owner_permissions

        if witness_permissions:
            data['witness'] = witness_permissions

        if actives_permissions:
            if len(actives_permissions) == 1:
                data['actives'] = actives_permissions[0]
            else:
                data['actives'] = actives_permissions

        return self.tron.manager.request('wallet/accountpermissionupdate', data)


================================================
FILE: tronapi/trx.py
================================================
# --------------------------------------------------------------------
# Copyright (c) iEXBase. All rights reserved.
# Licensed under the MIT License.
# See License.txt in the project root for license information.
# --------------------------------------------------------------------

"""
    tronapi.trx
    ===============

    Work with basic methods

    :copyright: © 2018 by the iEXBase.
    :license: MIT License
"""

import math
from typing import Any

from trx_utils import is_integer, is_hex
from trx_utils.types import is_object, is_string, is_list

from tronapi.common.transactions import wait_for_transaction_id
from tronapi.contract import Contract
from tronapi.exceptions import InvalidTronError, TronError, TimeExhausted
from tronapi.module import Module
from tronapi.common.blocks import select_method_for_block
from tronapi.common.toolz import (
    assoc
)
from tronapi.common.account import Account

TRX_MESSAGE_HEADER = '\x19TRON Signed Message:\n'
ETH_MESSAGE_HEADER = '\x19Ethereum Signed Message:\n'


class Trx(Module):
    default_contract_factory = Contract

    def get_current_block(self):
        """Query the latest block"""
        return self.tron.manager.request(url='/wallet/getnowblock')

    def get_confirmed_current_block(self):
        """Query the confirmed latest block"""
        return self.tron.manager.request('/walletsolidity/getnowblock')

    def get_block(self, block: Any = None):
        """Get block details using HashString or blockNumber

        Args:
            block (Any): ID or height for the block

        """

        # If the block identifier is not specified,
        # we take the default
        if block is None:
            block = self.tron.default_block

        if block == 'latest':
            return self.get_current_block()
        elif block == 'earliest':
            return self.get_block(0)

        method = select_method_for_block(
            block,
            if_hash={'url': '/wallet/getblockbyid', 'field': 'value'},
            if_number={'url': '/wallet/getblockbynum', 'field': 'num'},
        )

        result = self.tron.manager.request(method['url'], {
            method['field']: block
        })

        if result:
            return result
        raise ValueError("The call to {method['url']} did not return a value.")

    def get_transaction_count_by_blocknum(self, num: int):
        """Query transaction's count on a specified block by height

        Args:
            num (int): block number
        """
        if not is_integer(num) or num < 0:
            raise ValueError('Invalid num provided')

        return self.tron.manager.request('/wallet/gettransactioncountbyblocknum', {
            'num': num
        })

    def get_block_transaction_count(self, block: Any):
        """Total number of transactions in a block

        Args:
            block (Any): Number or Hash Block

        """
        transaction = self.get_block(block)
        if 'transactions' not in transaction:
            raise TronError('Parameter "transactions" not found')

        return len(transaction)

    def get_transaction_from_block(self, block: Any, index: int = 0):
        """Get transaction details from Block

        Args:
            block (Any): Number or Hash Block
            index (int) Position

        """
        if not is_integer(index) or index < 0:
            raise InvalidTronError('Invalid transaction index provided')

        transactions = self.get_block(block).get('transactions')
        if not transactions or len(transactions) < index:
            raise TronError('Transaction not found in block')

        return transactions[index]

    def wait_for_transaction_id(self,
                                transaction_hash: str,
                                timeout=120,
                                poll_latency=0.2):
        """
        Waits for the transaction specified by transaction_hash
        to be included in a block, then returns its transaction receipt.

        Optionally, specify a timeout in seconds.
        If timeout elapses before the transaction is added to a block,
        then wait_for_transaction_id() raises a Timeout exception.


        Args:
            transaction_hash (str): Transaction Hash
            timeout (int): TimeOut
            poll_latency (any):  between subsequent requests

        """
        try:
            if poll_latency > timeout:
                poll_latency = timeout

            return wait_for_transaction_id(self.tron, transaction_hash, timeout, poll_latency)
        except TimeoutError:
            raise TimeExhausted(
                "Transaction {} is not in the chain, after {} seconds".format(
                    transaction_hash,
                    timeout,
                )
            )

    def get_transaction(self, transaction_id: str,
                        is_confirm: bool = False):
        """Query transaction based on id

        Args:
            transaction_id (str): transaction id
            is_confirm (bool):
        """

        method = 'walletsolidity' if is_confirm else 'wallet'
        response = self.tron.manager.request('/{}/gettransactionbyid'.format(method), {
            'value': transaction_id
        })

        if 'txID' not in response:
            raise ValueError('Transaction not found')

        return response

    def get_account_by_id(self, account_id: str, options: object):
        return self.get_account_info_by_id(account_id, options)

    def get_account_info_by_id(self, account_id: str, options: object):

        if account_id.startswith('0x'):
            account_id = id[2:]

        if 'confirmed' in options:
            return self.tron.manager.request('/walletsolidity/getaccountbyid', {
                'account_id': self.tron.toHex(text=account_id)
            })

        return self.tron.manager.request('/wallet/getaccountbyid', {
            'account_id': self.tron.toHex(text=account_id)
        })

    def get_unconfirmed_account_by_id(self, account_id: str):

        return self.get_account_info_by_id(account_id, {
            'confirmed': True
        })

    def get_account_resource(self, address=None):
        """Query the resource information of the account

        Args:
            address (str): Address

        Results:
            Resource information of the account

        """

        if address is None:
            address = self.tron.default_address.hex

        if not self.tron.isAddress(address):
            raise InvalidTronError('Invalid address provided')

        return self.tron.manager.request('/wallet/getaccountresource', {
            'address': self.tron.address.to_hex(address)
        })

    def get_account(self, address=None):
        """Query information about an account

        Args:
            address (str): Address

        """

        if address is None:
            address = self.tron.default_address.hex

        if not self.tron.isAddress(address):
            raise InvalidTronError('Invalid address provided')

        return self.tron.manager.request('/walletsolidity/getaccount', {
            'address': self.tron.address.to_hex(address)
        })

    def get_balance(self, address=None, is_float=False):
        """Getting a balance

        Args:
            address (str): Address
            is_float (bool): Convert to float format

        """
        response = self.get_account(address)
        if 'balance' not in response:
            return 0

        if is_float:
            return self.tron.fromSun(response['balance'])

        return response['balance']

    def get_transactions_related(self, address, direction='all', limit=30, offset=0):
        """Getting data in the "from", "to" and "all" directions

        Args:
            address (str): Address
            direction (str): Type direction
            address (str): address
            limit (int): number of transactions expected to be returned
            offset (int): index of the starting transaction

        """

        if direction not in ['from', 'to', 'all']:
            raise InvalidTronError('Invalid direction provided: Expected "to", "from" or "all"')

        if direction == 'all':
            _from = self.get_transactions_related(address, 'from', limit, offset)
            _to = self.get_transactions_related(address, 'to', limit, offset)

            filter_from = [{**i, 'direction': 'from'} for i in _from]
            filter_to = [{**i, 'direction': 'to'} for i in _to]

            callback = filter_from
            callback.extend(filter_to)
            return callback

        if address is None:
            address = self.tron.default_address.hex

        if not self.tron.isAddress(address):
            raise InvalidTronError('Invalid address provided')

        if not isinstance(limit, int) or limit < 0 or (offset and limit < 1):
            raise InvalidTronError('Invalid limit provided')

        if not isinstance(offset, int) or offset < 0:
            raise InvalidTronError('Invalid offset provided')

        path = '/walletextension/gettransactions{0}this'.format(direction)
        response = self.tron.manager.request(path, {
            'account': {
                'address': self.tron.address.to_hex(address)
            },
            'limit': limit,
            'offset': offset
        }, 'get')

        if 'transaction' in response:
            return response['transaction']
        return response

    def get_transactions_to_address(self, address=None, limit=30, offset=0):
        """Query the list of transactions received by an address

        Args:
            address (str): address
            limit (int): number of transactions expected to be returned
            offset (int): index of the starting transaction

        Returns:
            Transactions list

        """
        return self.get_transactions_related(address, 'to', limit, offset)

    def get_transactions_from_address(self, address=None, limit=30, offset=0):
        """Query the list of transactions sent by an address

        Args:
            address (str): address
            limit (int): number of transactions expected to be returned
            offset (int): index of the starting transaction

        Returns:
            Transactions list

        """
        return self.get_transactions_related(address, 'from', limit, offset)

    def get_transaction_info(self, tx_id):
        """Query transaction fee based on id

        Args:
            tx_id (str): Transaction Id

        Returns:
            Transaction fee,block height and block creation time

        """
        response = self.tron.manager.request('/walletsolidity/gettransactioninfobyid', {
            'value': tx_id
        })

        return response

    def get_band_width(self, address=None):
        """Query bandwidth information.

        Args:
            address (str): address

        Returns:
            Bandwidth information for the account.
            If a field doesn't appear, then the corresponding value is 0.
            {
                "freeNetUsed": 557,
                "freeNetLimit": 5000,
                "NetUsed": 353,
                "NetLimit": 5239157853,
                "TotalNetLimit": 43200000000,
                "TotalNetWeight": 41228
            }

        """

        if address is None:
            address = self.tron.default_address.hex

        if not self.tron.isAddress(address):
            raise InvalidTronError('Invalid address provided')

        response = self.tron.manager.request('/wallet/getaccountnet', {
            'address': self.tron.address.to_hex(address)
        })

        free_net_limit = 0 if 'freeNetLimit' not in response else response['freeNetLimit']
        free_net_used = 0 if 'freeNetUsed' not in response else response['freeNetUsed']
        net_limit = 0 if 'NetLimit' not in response else response['NetLimit']
        net_used = 0 if 'NetUsed' not in response else response['NetUsed']

        return (free_net_limit - free_net_used) + (net_limit - net_used)

    def get_transaction_count(self):
        """Count all transactions on the network
        Note: Possible delays

        Returns:
            Total number of transactions.

        """
        response = self.tron.manager.request('/wallet/totaltransaction')
        return response.get('num')

    def send(self, to, amount, options=None):
        """Send funds to the Tron account (option 2)"""
        return self.send_transaction(to, amount, options)

    def send_trx(self, to, amount, options=None):
        """Send funds to the Tron account (option 3)"""
        return self.send_transaction(to, amount, options)

    def send_transaction(self, to, amount, options=None):
        """Send an asset to another account.
        Will create and broadcast the transaction if a private key is provided.

        Args:
            to (str): Address to send TRX to.
            amount (float): Amount of TRX to send.
            options (Any, optional): Options

        """

        if options is None:
            options = {}

        if 'from' not in options:
            options = assoc(options, 'from', self.tron.default_address.hex)

        tx = self.tron.transaction_builder.send_transaction(
            to,
            amount,
            options['from']
        )
        # If a comment is attached to the transaction,
        # in this case adding to the object
        if 'message' in options:
            tx['raw_data']['data'] = self.tron.toHex(text=str(options['message']))

        sign = self.sign(tx)
        result = self.broadcast(sign)

        return result

    def send_token(self, to, amount, token_id=None, account=None):
        """Transfer Token

        Args:
            to (str): is the recipient address
            amount (float): is the amount of token to transfer
            token_id (str): Token Name(NOT SYMBOL)
            account: (str): is the address of the withdrawal account

        Returns:
            Token transfer Transaction raw data

        """
        if account is None:
            account = self.tron.default_address.hex

        tx = self.tron.transaction_builder.send_token(
            to,
            amount,
            token_id,
            account
        )
        sign = self.sign(tx)
        result = self.broadcast(sign)

        return result

    def freeze_balance(self, amount=0, duration=3, resource='BANDWIDTH', account=None):
        """
        Freezes an amount of TRX.
        Will give bandwidth OR Energy and TRON Power(voting rights)
        to the owner of the frozen tokens.

        Args:
            amount (int): number of frozen trx
            duration (int): duration in days to be frozen
            resource (str): type of resource, must be either "ENERGY" or "BANDWIDTH"
            account (str): address that is freezing trx account

        """

        if account is None:
            account = self.tron.default_address.hex

        transaction = self.tron.transaction_builder.freeze_balance(
            amount,
            duration,
            resource,
            account
        )
        sign = self.sign(transaction)
        response = self.broadcast(sign)

        return response

    def unfreeze_balance(self, resource='BANDWIDTH', account=None):
        """
        Unfreeze TRX that has passed the minimum freeze duration.
        Unfreezing will remove bandwidth and TRON Power.

        Args:
            resource (str): type of resource, must be either "ENERGY" or "BANDWIDTH"
            account (str): address that is freezing trx account

        """

        if account is None:
            account = self.tron.default_address.hex

        transaction = self.tron.transaction_builder.unfreeze_balance(
            resource,
            account
        )
        sign = self.sign(transaction)
        response = self.broadcast(sign)

        return response

    def online_sign(self, transaction: dict):
        """Online transaction signature
        Sign the transaction, the api has the risk of leaking the private key,
        please make sure to call the api in a secure environment

        Warnings:
            Do not use this in any web / user-facing applications.
            This will expose the private key.

        Args:
            transaction (dict): transaction details

        """

        if 'signature' in transaction:
            raise TronError('Transaction is already signed')

        address = self.tron.address.from_private_key(self.tron.private_key).hex.lower()
        owner_address = transaction['raw_data']['contract'][0]['parameter']['value']['owner_address']

        if address != owner_address:
            raise ValueError('Private key does not match address in transaction')

        return self.tron.manager.request('/wallet/gettransactionsign', {
            'transaction': transaction,
            'privateKey': self.tron.private_key
        })

    def sign(self, transaction: Any, use_tron: bool = True, multisig: bool = False):
        """Safe method for signing your transaction

        Warnings:
            method: online_sign() - Use only in extreme cases.

        Args:
            transaction (Any): transaction details
            use_tron (bool): is Tron header
            multisig (bool): multi sign

        """

        if is_string(transaction):
            if not is_hex(transaction):
                raise TronError('Expected hex message input')

            # Determine which header to attach to the message
            # before encrypting or decrypting
            header = TRX_MESSAGE_HEADER if use_tron else ETH_MESSAGE_HEADER
            header += str(len(transaction))

            message_hash = self.tron.keccak(text=header+transaction)

            signed_message = Account.sign_hash(self.tron.toHex(message_hash), self.tron.private_key)
            return signed_message

        if not multisig and 'signature' in transaction:
            raise TronError('Transaction is already signed')

        try:
            if not multisig:
                address = self.tron.address.from_private_key(self.tron.private_key).hex.lower()
                owner_address = transaction['raw_data']['contract'][0]['parameter']['value']['owner_address']

                if address != owner_address:
                    raise ValueError('Private key does not match address in transaction')

            # This option deals with signing of transactions, and writing to the array
            signed_tx = Account.sign_hash(
                transaction['txID'], self.tron.private_key
            )
            signature = signed_tx['signature'].hex()[2:]

            # support multi sign
            if 'signature' in transaction and is_list(transaction['signature']):
                if not transaction['signature'].index(signature):
                    transaction['signature'].append(signature)
            else:
                transaction['signature'] = [signature]

            return transaction
        except ValueError as err:
            raise InvalidTronError(err)

    def broadcast(self, signed_transaction):
        """Broadcast the signed transaction

        Args:
            signed_transaction (object): signed transaction contract data

        """
        if not is_object(signed_transaction):
            raise InvalidTronError('Invalid transaction provided')

        if 'signature' not in signed_transaction:
            raise TronError('Transaction is not signed')

        response = self.tron.manager.request('/wallet/broadcasttransaction',
                                             signed_transaction)

        if 'result' in response:
            response.update({
                'transaction': signed_transaction
            })
        return response

    def sign_and_broadcast(self, transaction: Any):
        """Sign and send to the network

        Args:
            transaction (Any): transaction details
        """
        if not is_object(transaction):
            raise TronError('Invalid transaction provided')

        signed_tx = self.sign(transaction)
        return self.broadcast(signed_tx)

    def verify_message(self, message, signed_message=None, address=None, use_tron: bool = True):
        """ Get the address of the account that signed the message with the given hash.
        You must specify exactly one of: vrs or signature

        Args:
            message (str): The message in the format "hex"
            signed_message (AttributeDict): Signature
            address (str): is Address
            use_tron (bool): is Tron header

        """
        if address is None:
            address = self.tron.default_address.base58

        if not is_hex(message):
            raise TronError('Expected hex message input')

        # Determine which header to attach to the message
        # before encrypting or decrypting
        header = TRX_MESSAGE_HEADER if use_tron else ETH_MESSAGE_HEADER
        header += str(len(message))

        message_hash = self.tron.keccak(text=header+message)
        recovered = Account.recover_hash(self.tron.toHex(message_hash), signed_message.signature)

        tron_address = '41' + recovered[2:]
        base58address = self.tron.address.from_hex(tron_address).decode()

        if base58address == address:
            return True

        raise ValueError('Signature does not match')

    def update_account(self, account_name, address=None):
        """Modify account name
        Note: Username is allowed to edit only once.

        Args:
            account_name (str): name of the account
            address (str): address

        """
        if address is None:
            address = self.tron.default_address.hex

        transaction = self.tron.transaction_builder.update_account(
            account_name,
            address
        )
        sign = self.sign(transaction)
        response = self.broadcast(sign)

        return response

    def apply_for_sr(self, url, address):
        """Apply to become a super representative
        Note: Applied to become a super representative. Cost 9999 TRX.

        Args:
            url (str): official website address
            address (str): address

        """

        if address is None:
            address = self.tron.default_address.hex

        transaction = self.tron.transaction_builder.apply_for_sr(
            url,
            address
        )
        sign = self.sign(transaction)
        response = self.broadcast(sign)

        return response

    def list_nodes(self):
        """List the nodes which the api fullnode is connecting on the network"""
        response = self.tron.manager.request('/wallet/listnodes')
        callback = map(lambda x: {
            'address': '{}:{}'.format(self.tron.toText(x['address']['host']),
                                      str(x['address']['port']))
        }, response['nodes'])

        return list(callback)

    def get_tokens_issued_by_address(self, address):
        """List the tokens issued by an account.

        Args:
            address (str): address

        Returns:
            The token issued by the account.
            An account can issue only one token.

        """

        if not self.tron.isAddress(address):
            raise InvalidTronError('Invalid address provided')

        address = self.tron.address.to_hex(address)

        return self.tron.manager.request('/wallet/getassetissuebyaccount', {
            'address': address
        })

    def get_token_from_id(self, token_id: str):
        """Query token by name.

        Args:
            token_id (str): The name of the token

        """
        if not isinstance(token_id, str) or not len(token_id):
            raise InvalidTronError('Invalid token ID provided')

        return self.tron.manager.request('/wallet/getassetissuebyname', {
            'value': self.tron.toHex(text=token_id)
        })

    def get_block_range(self, start, end):
        """Query a range of blocks by block height

        Args:
            start (int): starting block height, including this block
            end (int): ending block height, excluding that block

        """
        if not is_integer(start) or start < 0:
            raise InvalidTronError('Invalid start of range provided')

        if not is_integer(end) or end <= start:
            raise InvalidTronError('Invalid end of range provided')

        response = self.tron.manager.request('/wallet/getblockbylimitnext', {
            'startNum': int(start),
            'endNum': int(end) + 1
        }, 'post')

        return response.get('block')

    def get_latest_blocks(self, num=1):
        """Query the latest blocks

        Args:
            num (int): the number of blocks to query

        """
        if not is_integer(num) or num <= 0:
            raise InvalidTronError('Invalid limit provided')

        response = self.tron.manager.request('/wallet/getblockbylatestnum', {
            'num': num
        })

        return response.get('block')

    def list_super_representatives(self):
        """Query the list of Super Representatives"""
        response = self.tron.manager.request('/wallet/listwitnesses')
        return response.get('witnesses')

    def list_tokens(self, limit=0, offset=0):
        """Query the list of Tokens with pagination

        Args:
            limit (int): index of the starting Token
            offset (int): number of Tokens expected to be returned

        Returns:
            List of Tokens

        """
        if not is_integer(limit) or (limit and offset < 1):
            raise InvalidTronError('Invalid limit provided')

        if not is_integer(offset) or offset < 0:
            raise InvalidTronError('Invalid offset provided')

        if not limit:
            return self.tron.manager.request('/wallet/getassetissuelist').get('assetIssue')

        return self.tron.manager.request('/wallet/getpaginatedassetissuelist', {
            'limit': int(limit),
            'offset': int(offset)
        })

    def time_until_next_vote_cycle(self):
        """Get the time of the next Super Representative vote

        Returns:
            Number of milliseconds until the next voting time.

        """
        num = self.tron.manager.request('/wallet/getnextmaintenancetime').get('num')

        if num == -1:
            raise Exception('Failed to get time until next vote cycle')

        return math.floor(num / 1000)

    def get_contract(self, contract_address):
        """Queries a contract's information from the blockchain.

        Args:
            contract_address (str): contract address

        Returns:
            SmartContract object.

        """

        if not self.tron.isAddress(contract_address):
            raise InvalidTronError('Invalid contract address provided')

        return self.tron.manager.request('/wallet/getcontract', {
            'value': self.tron.address.to_hex(contract_address)
        })

    def contract(self, address=None, **kwargs):
        """Work with a contract

        Args:
            address (str): TRON Address
            **kwargs (any): details (bytecode, abi)
        """
        factory_class = kwargs.pop('contract_factory_class', self.default_contract_factory)
        contract_factory = factory_class.factory(self.tron, **kwargs)

        if address:
            return contract_factory(address)
        return contract_factory

    def validate_address(self, address, _is_hex=False):
        """Validate address

        Args:
            address (str): The address, should be in base58checksum
            _is_hex (bool): hexString or base64 format

        """
        if _is_hex:
            address = self.tron.address.to_hex(address)

        return self.tron.manager.request('/wallet/validateaddress', {
            'address': address
        })

    def get_chain_parameters(self):
        """Getting chain parameters"""
        return self.tron.manager.request('/wallet/getchainparameters')

    def get_exchange_by_id(self, exchange_id):
        """Find exchange by id

        Args:
             exchange_id (str): ID Exchange

        """

        if not isinstance(exchange_id, int) or exchange_id < 0:
            raise InvalidTronError('Invalid exchangeID provided')

        return self.tron.manager.request('/wallet/getexchangebyid', {
            'id': exchange_id
        })

    def get_list_exchangers(self):
        """Get list exchangers"""
        return self.tron.manager.request('/wallet/listexchanges')

    def get_proposal(self, proposal_id):
        """Query proposal based on id

        Args:
            proposal_id (int): ID

        """
        if not isinstance(proposal_id, int) or proposal_id < 0:
            raise InvalidTronError('Invalid proposalID provided')

        return self.tron.manager.request('/wallet/getproposalbyid', {
            'id': int(proposal_id)
        })

    def list_proposals(self):
        """Query all proposals

        Returns:
            Proposal list information

        """
        return self.tron.manager.request('/wallet/listproposals')

    def vote_proposal(self, proposal_id, has_approval, voter_address):
        """Proposal approval

        Args:
            proposal_id (int): proposal id
            has_approval (bool): Approved
            voter_address (str): Approve address

        Returns:
             Approval of the proposed transaction

        """

        if voter_address is None:
            voter_address = self.tron.default_address.hex

        transaction = self.tron.transaction_builder.vote_proposal(
            proposal_id,
            has_approval,
            voter_address
        )
        sign = self.sign(transaction)
        response = self.broadcast(sign)

        return response

    def proposal_delete(self, proposal_id: int, issuer_address: str):
        """Delete proposal

        Args:
            proposal_id (int): proposal id
            issuer_address (str): delete the person's address

        Results:
            Delete the proposal's transaction

        """
        if issuer_address is None:
            issuer_address = self.tron.default_address.hex

        transaction = self.tron.transaction_builder.delete_proposal(
            proposal_id,
            issuer_address
        )
        sign = self.sign(transaction)
        response = self.broadcast(sign)

        return response

    def list_exchanges_paginated(self, limit=10, offset=0):
        """Paged query transaction pair list

        Args:
            limit (int): number of trading pairs  expected to be returned.
            offset (int): index of the starting trading pair

        """
        return self.tron.manage
Download .txt
gitextract_aju8mwnx/

├── .bumpversion.cfg
├── .coveragerc
├── .gitignore
├── .pyup.yml
├── .travis.yml
├── LICENSE
├── README.rst
├── codecov.yml
├── docs/
│   ├── index.rst
│   ├── overview.rst
│   ├── quickstart.rst
│   ├── releases.rst
│   └── v2_migration.rst
├── example.py
├── examples/
│   ├── account.py
│   ├── address-hex.py
│   ├── amount.py
│   ├── contract.py
│   ├── custom-nodes.py
│   ├── find-transaction.py
│   ├── send-transaction.py
│   └── sign.py
├── requirements-docs.txt
├── setup.py
└── tronapi/
    ├── __init__.py
    ├── common/
    │   ├── __init__.py
    │   ├── abi.py
    │   ├── account.py
    │   ├── blocks.py
    │   ├── contracts.py
    │   ├── datastructures.py
    │   ├── datatypes.py
    │   ├── encoding.py
    │   ├── formatters.py
    │   ├── normalizers.py
    │   ├── threads.py
    │   ├── toolz/
    │   │   └── __init__.py
    │   ├── transactions.py
    │   └── validation.py
    ├── constants.py
    ├── contract.py
    ├── exceptions.py
    ├── main.py
    ├── manager.py
    ├── module.py
    ├── providers/
    │   ├── __init__.py
    │   ├── base.py
    │   └── http.py
    ├── transactionbuilder.py
    └── trx.py
Download .txt
SYMBOL INDEX (335 symbols across 21 files)

FILE: tronapi/common/abi.py
  function filter_by_argument_name (line 108) | def filter_by_argument_name(argument_names, contract_abi):
  function process_type (line 131) | def process_type(type_str):
  function collapse_type (line 168) | def collapse_type(base, sub, arrlist):
  function filter_by_type (line 172) | def filter_by_type(_type, contract_abi):
  function filter_by_name (line 176) | def filter_by_name(name, contract_abi):
  function get_abi_input_types (line 188) | def get_abi_input_types(abi):
  function get_abi_output_types (line 195) | def get_abi_output_types(abi):
  function get_abi_input_names (line 202) | def get_abi_input_names(abi):
  function length_of_array_type (line 209) | def length_of_array_type(abi_type):
  function get_fallback_func_abi (line 222) | def get_fallback_func_abi(contract_abi):
  function fallback_func_abi_exists (line 230) | def fallback_func_abi_exists(contract_abi):
  function get_constructor_abi (line 234) | def get_constructor_abi(contract_abi):
  function is_recognized_type (line 246) | def is_recognized_type(abi_type):
  function is_bool_type (line 250) | def is_bool_type(abi_type):
  function is_uint_type (line 254) | def is_uint_type(abi_type):
  function is_int_type (line 258) | def is_int_type(abi_type):
  function is_address_type (line 262) | def is_address_type(abi_type):
  function is_bytes_type (line 266) | def is_bytes_type(abi_type):
  function is_string_type (line 270) | def is_string_type(abi_type):
  function is_length (line 275) | def is_length(target_length, value):
  function size_of_type (line 279) | def size_of_type(abi_type):
  function is_array_type (line 296) | def is_array_type(abi_type):
  function sub_type_of_array_type (line 300) | def sub_type_of_array_type(abi_type):
  function is_probably_enum (line 309) | def is_probably_enum(abi_type):
  function normalize_event_input_types (line 314) | def normalize_event_input_types(abi_args):
  function abi_to_signature (line 324) | def abi_to_signature(abi):
  function filter_by_argument_count (line 334) | def filter_by_argument_count(num_arguments, contract_abi):
  function filter_by_encodability (line 343) | def filter_by_encodability(args, kwargs, contract_abi):
  class AcceptsHexStrMixin (line 351) | class AcceptsHexStrMixin:
    method validate_value (line 352) | def validate_value(self, value):
  class ByteStringEncoder (line 365) | class ByteStringEncoder(AcceptsHexStrMixin, encoding.ByteStringEncoder):
  class BytesEncoder (line 369) | class BytesEncoder(AcceptsHexStrMixin, encoding.BytesEncoder):
  class TextStringEncoder (line 373) | class TextStringEncoder(encoding.TextStringEncoder):
    method validate_value (line 375) | def validate_value(cls, value):
  function check_if_arguments_can_be_encoded (line 418) | def check_if_arguments_can_be_encoded(function_abi, args, kwargs):
  function merge_args_and_kwargs (line 435) | def merge_args_and_kwargs(function_abi, args, kwargs):
  function abi_sub_tree (line 491) | def abi_sub_tree(data_type, data_value):
  function map_abi_data (line 516) | def map_abi_data(normalizers, types, data):
  function abi_data_tree (line 550) | def abi_data_tree(types, data):
  function data_tree_map (line 569) | def data_tree_map(func, data_tree):
  class ABITypedData (line 584) | class ABITypedData(namedtuple('ABITypedData', 'abi_type, data')):
    method __new__ (line 603) | def __new__(cls, iterable):
  function strip_abi_type (line 607) | def strip_abi_type(elements):

FILE: tronapi/common/account.py
  class Account (line 19) | class Account:
    method create (line 21) | def create():
    method sign_hash (line 26) | def sign_hash(message_hash, private_key):
    method recover_hash (line 33) | def recover_hash(message_hash, signature):
  class Address (line 40) | class Address(object):
    method from_hex (line 42) | def from_hex(address):
    method to_hex (line 50) | def to_hex(address):
    method from_private_key (line 58) | def from_private_key(private_key):
  class PrivateKey (line 62) | class PrivateKey(object):
    method __init__ (line 63) | def __init__(self, private_key):
    method private_key (line 79) | def private_key(self):
    method public_key (line 84) | def public_key(self) -> str:
    method address (line 89) | def address(self):
    method __str__ (line 103) | def __str__(self):
    method __bytes__ (line 106) | def __bytes__(self):

FILE: tronapi/common/blocks.py
  function is_hex_encoded_block_hash (line 13) | def is_hex_encoded_block_hash(value):
  function is_hex_encoded_block_number (line 19) | def is_hex_encoded_block_number(value):
  function select_method_for_block (line 31) | def select_method_for_block(value, if_hash, if_number):

FILE: tronapi/common/contracts.py
  class FallbackFn (line 47) | class FallbackFn:
  function find_matching_fn_abi (line 51) | def find_matching_fn_abi(abi, fn_identifier=None, args=None, kwargs=None):
  function encode_abi (line 109) | def encode_abi(tron, abi, arguments, data=None):
  function get_function_info (line 149) | def get_function_info(fn_name, contract_abi=None, fn_abi=None, args=None...

FILE: tronapi/common/datastructures.py
  class ReadableAttributeDict (line 22) | class ReadableAttributeDict(Mapping):
    method __init__ (line 27) | def __init__(self, dictionary, *args, **kwargs):
    method __getitem__ (line 31) | def __getitem__(self, key):
    method __iter__ (line 34) | def __iter__(self):
    method __len__ (line 37) | def __len__(self):
    method __repr__ (line 40) | def __repr__(self):
    method _repr_pretty_ (line 43) | def _repr_pretty_(self, builder, cycle):
    method _apply_if_mapping (line 55) | def _apply_if_mapping(cls, value):
    method recursive (line 62) | def recursive(cls, value):
  class MutableAttributeDict (line 66) | class MutableAttributeDict(MutableMapping, ReadableAttributeDict):
    method __setitem__ (line 68) | def __setitem__(self, key, val):
    method __delitem__ (line 71) | def __delitem__(self, key):
  class AttributeDict (line 75) | class AttributeDict(ReadableAttributeDict, Hashable):
    method __setattr__ (line 80) | def __setattr__(self, attr, val):
    method __delattr__ (line 86) | def __delattr__(self, key):
    method __hash__ (line 89) | def __hash__(self):
    method __eq__ (line 92) | def __eq__(self, other):
  class NamedElementOnion (line 99) | class NamedElementOnion(Mapping):
    method __init__ (line 104) | def __init__(self, init_elements, valid_element=callable):
    method add (line 112) | def add(self, element, name=None):
    method inject (line 124) | def inject(self, element, name=None, layer=None):
    method clear (line 155) | def clear(self):
    method replace (line 158) | def replace(self, old, new):
    method remove (line 169) | def remove(self, old):
    method _replace_with_new_name (line 174) | def _replace_with_new_name(self, old, new):
    method __iter__ (line 186) | def __iter__(self):
    method __add__ (line 192) | def __add__(self, other):
    method __contains__ (line 199) | def __contains__(self, element):
    method __getitem__ (line 202) | def __getitem__(self, element):
    method __len__ (line 205) | def __len__(self):
    method __reversed__ (line 208) | def __reversed__(self):

FILE: tronapi/common/datatypes.py
  function verify_attr (line 15) | def verify_attr(class_name, key, namespace):
  class PropertyCheckingFactory (line 24) | class PropertyCheckingFactory(type):
    method __init__ (line 25) | def __init__(cls, name, bases, namespace, **kargs):
    method __new__ (line 30) | def __new__(mcs, name, bases, namespace, normalizers=None):

FILE: tronapi/common/encoding.py
  function hex_encode_abi_type (line 50) | def hex_encode_abi_type(abi_type, value, force_size=None):
  function to_hex_twos_compliment (line 82) | def to_hex_twos_compliment(value, bit_size):
  function to_hex_with_size (line 95) | def to_hex_with_size(value, bit_size):
  function pad_hex (line 100) | def pad_hex(value, bit_size):
  function trim_hex (line 106) | def trim_hex(hexstr):
  function to_int (line 114) | def to_int(value=None, hexstr=None, text=None):
  function text_if_str (line 140) | def text_if_str(to_type, text_or_primitive):
  function to_text (line 149) | def to_text(primitive=None, hexstr=None, text=None):
  function to_bytes (line 166) | def to_bytes(primitive=None, hexstr=None, text=None):
  function to_4byte_hex (line 184) | def to_4byte_hex(hex_or_str_or_bytes: Union[int, str, bytes]) -> str:
  class FriendlyJsonSerialize (line 195) | class FriendlyJsonSerialize:
    method _json_mapping_errors (line 203) | def _json_mapping_errors(self, mapping):
    method _json_list_errors (line 210) | def _json_list_errors(self, iterable):
    method _friendly_json_encode (line 217) | def _friendly_json_encode(self, obj, cls=None):
    method json_decode (line 232) | def json_decode(json_str):
    method json_encode (line 242) | def json_encode(self, obj, cls=None):
  class TronJsonEncoder (line 249) | class TronJsonEncoder(json.JSONEncoder):
    method default (line 250) | def default(self, obj):
  function to_json (line 258) | def to_json(obj: object) -> object:

FILE: tronapi/common/formatters.py
  function apply_formatters_to_dict (line 22) | def apply_formatters_to_dict(formatters, value):
  function recursive_map (line 34) | def recursive_map(func, data):
  function map_collection (line 47) | def map_collection(func, collection):

FILE: tronapi/common/normalizers.py
  function implicitly_identity (line 25) | def implicitly_identity(to_wrap):
  function normalize_abi (line 37) | def normalize_abi(abi):
  function normalize_bytecode (line 44) | def normalize_bytecode(bytecode):
  function abi_address_to_hex (line 51) | def abi_address_to_hex(abi_type, data):
  function abi_string_to_text (line 59) | def abi_string_to_text(abi_type, data):
  function abi_bytes_to_bytes (line 65) | def abi_bytes_to_bytes(abi_type, data):
  function addresses_checksummed (line 72) | def addresses_checksummed(abi_type, data):
  function to_checksum_address (line 77) | def to_checksum_address(address: str):
  function abi_resolver (line 82) | def abi_resolver(abi_type, val):

FILE: tronapi/common/threads.py
  class Timeout (line 8) | class Timeout(Exception):
    method __init__ (line 17) | def __init__(self, seconds=None, exception=None, *args, **kwargs):
    method __enter__ (line 21) | def __enter__(self):
    method __exit__ (line 25) | def __exit__(self, exc_type, exc_val, exc_tb):
    method __str__ (line 28) | def __str__(self):
    method expire_at (line 34) | def expire_at(self):
    method start (line 41) | def start(self):
    method check (line 47) | def check(self):
    method cancel (line 63) | def cancel(self):
    method sleep (line 66) | def sleep(self, seconds):
  class ThreadWithReturn (line 71) | class ThreadWithReturn(threading.Thread):
    method __init__ (line 72) | def __init__(self, target=None, args=None, kwargs=None):
    method run (line 82) | def run(self):
    method get (line 85) | def get(self, timeout=None):
  class TimerClass (line 93) | class TimerClass(threading.Thread):
    method __init__ (line 94) | def __init__(self, interval, callback, *args):
    method run (line 101) | def run(self):
    method stop (line 106) | def stop(self):
  function spawn (line 110) | def spawn(target, *args, thread_class=ThreadWithReturn, **kwargs):

FILE: tronapi/common/transactions.py
  function wait_for_transaction_id (line 4) | def wait_for_transaction_id(tron, tx_id, timeout=120, poll_latency=0.1):

FILE: tronapi/common/validation.py
  function _prepare_selector_collision_msg (line 43) | def _prepare_selector_collision_msg(duplicates):
  function is_valid_url (line 50) | def is_valid_url(value):
  function validate_abi (line 69) | def validate_abi(abi):
  function validate_abi_type (line 92) | def validate_abi_type(abi_type):
  function validate_abi_value (line 100) | def validate_abi_value(abi_type, value):
  function validate_address (line 155) | def validate_address(value):
  function has_one_val (line 186) | def has_one_val(*args, **kwargs):
  function assert_one_val (line 192) | def assert_one_val(*args, **kwargs):

FILE: tronapi/contract.py
  class NonExistentFallbackFunction (line 51) | class NonExistentFallbackFunction:
    method _raise_exception (line 53) | def _raise_exception():
    method __getattr__ (line 56) | def __getattr__(self, attr):
  class ContractFunction (line 60) | class ContractFunction:
    method __init__ (line 70) | def __init__(self, abi=None):
    method __call__ (line 74) | def __call__(self, *args, **kwargs):
    method _set_function_info (line 88) | def _set_function_info(self):
    method factory (line 106) | def factory(cls, class_name, **kwargs):
    method __repr__ (line 109) | def __repr__(self):
  class ContractFunctions (line 118) | class ContractFunctions:
    method __init__ (line 122) | def __init__(self, abi, tron, address=None):
    method __iter__ (line 137) | def __iter__(self):
    method __getattr__ (line 144) | def __getattr__(self, function_name):
    method __getitem__ (line 158) | def __getitem__(self, function_name):
  class Contract (line 162) | class Contract:
    method __init__ (line 178) | def __init__(self, address=None):
    method factory (line 198) | def factory(cls, tron, class_name=None, **kwargs):
    method deploy (line 221) | def deploy(cls, **kwargs):
    method constructor (line 247) | def constructor(cls):
    method encodeABI (line 258) | def encodeABI(cls, fn_name, args=None, kwargs=None, data=None):
    method get_fallback_function (line 272) | def get_fallback_function(abi, tron, address=None):
    method all_functions (line 284) | def all_functions(self):
    method get_function_by_signature (line 290) | def get_function_by_signature(self, signature):
    method find_functions_by_name (line 304) | def find_functions_by_name(self, fn_name):
    method get_function_by_name (line 313) | def get_function_by_name(self, fn_name):
    method get_function_by_selector (line 318) | def get_function_by_selector(self, selector):
    method decode_function_input (line 326) | def decode_function_input(self, data):
    method find_functions_by_args (line 337) | def find_functions_by_args(self, *args):
    method get_function_by_args (line 346) | def get_function_by_args(self, *args):
  class ContractConstructor (line 351) | class ContractConstructor:
    method __init__ (line 356) | def __init__(self, tron, abi, bytecode):
    method check_forbidden_keys_in_transaction (line 362) | def check_forbidden_keys_in_transaction(transaction, forbidden_keys=No...
    method transact (line 368) | def transact(self, **kwargs):
  function find_functions_by_identifier (line 385) | def find_functions_by_identifier(contract_abi, tron, address, callable_c...
  function get_function_by_identifier (line 401) | def get_function_by_identifier(fns, identifier):

FILE: tronapi/exceptions.py
  class TronError (line 8) | class TronError(Exception):
  class InvalidTronError (line 12) | class InvalidTronError(TronError):
  class FallbackNotFound (line 16) | class FallbackNotFound(Exception):
  class MismatchedABI (line 23) | class MismatchedABI(Exception):
  class InvalidAddress (line 31) | class InvalidAddress(ValueError):
  class NoABIFunctionsFound (line 38) | class NoABIFunctionsFound(AttributeError):
  class ValidationError (line 45) | class ValidationError(Exception):
  class TransportError (line 52) | class TransportError(TronError):
    method status_code (line 61) | def status_code(self):
    method error (line 65) | def error(self):
    method info (line 69) | def info(self):
    method url (line 73) | def url(self):
  class HttpError (line 77) | class HttpError(TransportError):
  class BadRequest (line 81) | class BadRequest(TransportError):
  class NotFoundError (line 85) | class NotFoundError(TransportError):
  class ServiceUnavailable (line 89) | class ServiceUnavailable(TransportError):
  class GatewayTimeout (line 93) | class GatewayTimeout(TransportError):
  class TimeExhausted (line 97) | class TimeExhausted(Exception):

FILE: tronapi/main.py
  class Tron (line 63) | class Tron:
    method __init__ (line 85) | def __init__(self, **kwargs):
    method default_block (line 128) | def default_block(self):
    method default_block (line 132) | def default_block(self, block_id):
    method providers (line 144) | def providers(self):
    method private_key (line 149) | def private_key(self):
    method private_key (line 154) | def private_key(self, value: str) -> None:
    method default_address (line 169) | def default_address(self) -> AttributeDict:
    method default_address (line 174) | def default_address(self, address: str) -> None:
    method get_event_result (line 199) | def get_event_result(self, **kwargs):
    method get_event_transaction_id (line 265) | def get_event_transaction_id(self, tx_id):
    method address (line 275) | def address(self) -> Address:
    method create_account (line 287) | def create_account(self) -> PrivateKey:
    method is_valid_provider (line 298) | def is_valid_provider(provider) -> bool:
    method solidity_sha3 (line 306) | def solidity_sha3(self, abi_types, values):
    method keccak (line 339) | def keccak(primitive=None, text=None, hexstr=None):
    method is_connected (line 353) | def is_connected(self):

FILE: tronapi/manager.py
  class TronManager (line 32) | class TronManager(object):
    method __init__ (line 40) | def __init__(self, tron, providers):
    method providers (line 66) | def providers(self):
    method providers (line 73) | def providers(self, value) -> None:
    method full_node (line 80) | def full_node(self) -> HttpProvider:
    method solidity_node (line 89) | def solidity_node(self) -> HttpProvider:
    method event_server (line 98) | def event_server(self) -> HttpProvider:
    method request (line 106) | def request(self, url, params=None, method=None):
    method is_connected (line 130) | def is_connected(self):

FILE: tronapi/module.py
  class Module (line 8) | class Module:
    method __init__ (line 11) | def __init__(self, tron) -> None:
    method attach (line 15) | def attach(cls, target, module_name: str = None) -> None:

FILE: tronapi/providers/base.py
  class BaseProvider (line 11) | class BaseProvider(object):
    method status_page (line 15) | def status_page(self):
    method status_page (line 20) | def status_page(self, page):
    method _http_default_headers (line 24) | def _http_default_headers():
    method format_user_agent (line 32) | def format_user_agent(name=None):

FILE: tronapi/providers/http.py
  class HttpProvider (line 37) | class HttpProvider(BaseProvider):
    method __init__ (line 40) | def __init__(self, node_url, request_kwargs=None):
    method get_request_kwargs (line 66) | def get_request_kwargs(self):
    method request (line 73) | def request(self, path, json=None, params=None, method=None):
    method is_connected (line 96) | def is_connected(self) -> bool:
    method _request (line 112) | def _request(self, **kwargs):
    method __error_manager (line 140) | def __error_manager(data):

FILE: tronapi/transactionbuilder.py
  class TransactionBuilder (line 34) | class TransactionBuilder(object):
    method __init__ (line 35) | def __init__(self, tron):
    method send_transaction (line 38) | def send_transaction(self, to, amount, account=None):
    method send_token (line 76) | def send_token(self, to, amount, token_id, account=None):
    method freeze_balance (line 124) | def freeze_balance(self, amount, duration, resource, account=None):
    method unfreeze_balance (line 166) | def unfreeze_balance(self, resource='BANDWIDTH', account=None):
    method purchase_token (line 197) | def purchase_token(self, to: str, token_id: str, amount: int, buyer=No...
    method withdraw_block_rewards (line 231) | def withdraw_block_rewards(self, address: str = None):
    method apply_for_sr (line 249) | def apply_for_sr(self, url, address=None):
    method vote (line 273) | def vote(self, votes: List[Tuple[str, int]], voter_address: str = None):
    method create_proposal (line 317) | def create_proposal(self, parameters: Any, issuer_address=None):
    method vote_proposal (line 347) | def vote_proposal(self, proposal_id, has_approval, voter_address=None):
    method delete_proposal (line 376) | def delete_proposal(self, proposal_id: int, issuer_address: str = None):
    method update_account (line 403) | def update_account(self, account_name, account: str = None):
    method create_smart_contract (line 434) | def create_smart_contract(self, **kwargs):
    method trigger_smart_contract (line 508) | def trigger_smart_contract(self, **kwargs):
    method create_trx_exchange (line 599) | def create_trx_exchange(self,
    method create_token_exchange (line 633) | def create_token_exchange(self,
    method inject_exchange_tokens (line 668) | def inject_exchange_tokens(self,
    method create_token (line 702) | def create_token(self, **kwargs):
    method withdraw_exchange_tokens (line 837) | def withdraw_exchange_tokens(self,
    method trade_exchange_tokens (line 871) | def trade_exchange_tokens(self,
    method update_setting (line 912) | def update_setting(self,
    method update_energy_limit (line 946) | def update_energy_limit(self,
    method check_permissions (line 980) | def check_permissions(self, permissions, _type):
    method update_account_permissions (line 998) | def update_account_permissions(self, owner_address=None,

FILE: tronapi/trx.py
  class Trx (line 37) | class Trx(Module):
    method get_current_block (line 40) | def get_current_block(self):
    method get_confirmed_current_block (line 44) | def get_confirmed_current_block(self):
    method get_block (line 48) | def get_block(self, block: Any = None):
    method get_transaction_count_by_blocknum (line 80) | def get_transaction_count_by_blocknum(self, num: int):
    method get_block_transaction_count (line 93) | def get_block_transaction_count(self, block: Any):
    method get_transaction_from_block (line 106) | def get_transaction_from_block(self, block: Any, index: int = 0):
    method wait_for_transaction_id (line 123) | def wait_for_transaction_id(self,
    method get_transaction (line 155) | def get_transaction(self, transaction_id: str,
    method get_account_by_id (line 174) | def get_account_by_id(self, account_id: str, options: object):
    method get_account_info_by_id (line 177) | def get_account_info_by_id(self, account_id: str, options: object):
    method get_unconfirmed_account_by_id (line 191) | def get_unconfirmed_account_by_id(self, account_id: str):
    method get_account_resource (line 197) | def get_account_resource(self, address=None):
    method get_account (line 218) | def get_account(self, address=None):
    method get_balance (line 236) | def get_balance(self, address=None, is_float=False):
    method get_transactions_related (line 253) | def get_transactions_related(self, address, direction='all', limit=30,...
    method get_transactions_to_address (line 304) | def get_transactions_to_address(self, address=None, limit=30, offset=0):
    method get_transactions_from_address (line 318) | def get_transactions_from_address(self, address=None, limit=30, offset...
    method get_transaction_info (line 332) | def get_transaction_info(self, tx_id):
    method get_band_width (line 348) | def get_band_width(self, address=None):
    method get_transaction_count (line 385) | def get_transaction_count(self):
    method send (line 396) | def send(self, to, amount, options=None):
    method send_trx (line 400) | def send_trx(self, to, amount, options=None):
    method send_transaction (line 404) | def send_transaction(self, to, amount, options=None):
    method send_token (line 436) | def send_token(self, to, amount, token_id=None, account=None):
    method freeze_balance (line 463) | def freeze_balance(self, amount=0, duration=3, resource='BANDWIDTH', a...
    method unfreeze_balance (line 491) | def unfreeze_balance(self, resource='BANDWIDTH', account=None):
    method online_sign (line 514) | def online_sign(self, transaction: dict):
    method sign (line 542) | def sign(self, transaction: Any, use_tron: bool = True, multisig: bool...
    method broadcast (line 597) | def broadcast(self, signed_transaction):
    method sign_and_broadcast (line 619) | def sign_and_broadcast(self, transaction: Any):
    method verify_message (line 631) | def verify_message(self, message, signed_message=None, address=None, u...
    method update_account (line 664) | def update_account(self, account_name, address=None):
    method apply_for_sr (line 685) | def apply_for_sr(self, url, address):
    method list_nodes (line 707) | def list_nodes(self):
    method get_tokens_issued_by_address (line 717) | def get_tokens_issued_by_address(self, address):
    method get_token_from_id (line 738) | def get_token_from_id(self, token_id: str):
    method get_block_range (line 752) | def get_block_range(self, start, end):
    method get_latest_blocks (line 773) | def get_latest_blocks(self, num=1):
    method list_super_representatives (line 789) | def list_super_representatives(self):
    method list_tokens (line 794) | def list_tokens(self, limit=0, offset=0):
    method time_until_next_vote_cycle (line 819) | def time_until_next_vote_cycle(self):
    method get_contract (line 833) | def get_contract(self, contract_address):
    method contract (line 851) | def contract(self, address=None, **kwargs):
    method validate_address (line 865) | def validate_address(self, address, _is_hex=False):
    method get_chain_parameters (line 880) | def get_chain_parameters(self):
    method get_exchange_by_id (line 884) | def get_exchange_by_id(self, exchange_id):
    method get_list_exchangers (line 899) | def get_list_exchangers(self):
    method get_proposal (line 903) | def get_proposal(self, proposal_id):
    method list_proposals (line 917) | def list_proposals(self):
    method vote_proposal (line 926) | def vote_proposal(self, proposal_id, has_approval, voter_address):
    method proposal_delete (line 952) | def proposal_delete(self, proposal_id: int, issuer_address: str):
    method list_exchanges_paginated (line 975) | def list_exchanges_paginated(self, limit=10, offset=0):
    method get_node_info (line 988) | def get_node_info(self):
    method get_token_list_name (line 992) | def get_token_list_name(self, token_id: str) -> any:
    method get_token_by_id (line 1005) | def get_token_by_id(self, token_id: str) -> any:
Condensed preview — 50 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (205K chars).
[
  {
    "path": ".bumpversion.cfg",
    "chars": 464,
    "preview": "[bumpversion]\ncurrent_version = 3.1.5\ncommit = True\ntag = True\nparse = (?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)(-("
  },
  {
    "path": ".coveragerc",
    "chars": 41,
    "preview": "[run]\nbranch = True\ninclude = */tronapi/*"
  },
  {
    "path": ".gitignore",
    "chars": 1210,
    "preview": ".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 / "
  },
  {
    "path": ".pyup.yml",
    "chars": 228,
    "preview": "# autogenerated pyup.io config file\n# see https://pyup.io/docs/configuration/ for all available options\n\nupdate: all\n\n# "
  },
  {
    "path": ".travis.yml",
    "chars": 360,
    "preview": "sudo: required\nlanguage: python\nos: linux\ncache: pip\ndist: trusty\npython:\n  - \"3.6\"\n  - \"3.7-dev\"\n  - \"nightly\" # curren"
  },
  {
    "path": "LICENSE",
    "chars": 1064,
    "preview": "MIT License\n\nCopyright (c) 2018 iEXBase\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof"
  },
  {
    "path": "README.rst",
    "chars": 4586,
    "preview": "===================\nTRON API for Python\n===================\n\nA Python API for interacting with the Tron (TRX)\n\n.. image:"
  },
  {
    "path": "codecov.yml",
    "chars": 942,
    "preview": "# --------------------------------------------------------------------------------------------\n# Copyright (c) iEXBase. "
  },
  {
    "path": "docs/index.rst",
    "chars": 198,
    "preview": "TronAPI\n=======\n\nTronAPI is a python library for interacting with Tron Protocol.\n\nContents\n--------\n\n.. toctree::\n    :m"
  },
  {
    "path": "docs/overview.rst",
    "chars": 5158,
    "preview": "Overview\n========\n\n.. contents:: :local:\n\nThe common entrypoint for interacting with the Tron library is the ``Tron``\nob"
  },
  {
    "path": "docs/quickstart.rst",
    "chars": 2122,
    "preview": "Quickstart\n==========\n\n.. contents:: :local:\n\n.. NOTE:: All code starting with a ``$`` is meant to run on your terminal."
  },
  {
    "path": "docs/releases.rst",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "docs/v2_migration.rst",
    "chars": 1456,
    "preview": "Migrating your code from v1 to v2\n=======================================\n\nChanges to base API convenience methods\n-----"
  },
  {
    "path": "example.py",
    "chars": 4095,
    "preview": "import json\nimport logging\n\nfrom tronapi import Tron\n\nlogging.basicConfig(level=logging.DEBUG, format=\"%(asctime)s - %(l"
  },
  {
    "path": "examples/account.py",
    "chars": 913,
    "preview": "\nimport logging\nfrom tronapi import Tron\n\nlogging.basicConfig(level=logging.DEBUG, format=\"%(asctime)s - %(levelname)s -"
  },
  {
    "path": "examples/address-hex.py",
    "chars": 562,
    "preview": "from tronapi import Tron\nfrom tronapi import HttpProvider\n\nfull_node = HttpProvider('https://api.trongrid.io')\nsolidity_"
  },
  {
    "path": "examples/amount.py",
    "chars": 407,
    "preview": "from tronapi import Tron\nfrom tronapi import HttpProvider\n\nfull_node = HttpProvider('https://api.trongrid.io')\nsolidity_"
  },
  {
    "path": "examples/contract.py",
    "chars": 1085,
    "preview": "from tronapi import Tron, HttpProvider\nfrom solc import compile_source\n\nfull_node = HttpProvider('https://api.trongrid.i"
  },
  {
    "path": "examples/custom-nodes.py",
    "chars": 486,
    "preview": "from tronapi import Tron\nfrom tronapi import HttpProvider\n\nfull_node = HttpProvider('https://api.trongrid.io')\nsolidity_"
  },
  {
    "path": "examples/find-transaction.py",
    "chars": 380,
    "preview": "from tronapi import Tron\nfrom tronapi import HttpProvider\n\nfull_node = HttpProvider('https://api.trongrid.io')\nsolidity_"
  },
  {
    "path": "examples/send-transaction.py",
    "chars": 484,
    "preview": "from tronapi import Tron\nfrom tronapi import HttpProvider\n\nfull_node = HttpProvider('https://api.trongrid.io')\nsolidity_"
  },
  {
    "path": "examples/sign.py",
    "chars": 642,
    "preview": "from tronapi import Tron\nfrom tronapi import HttpProvider\n\nfull_node = HttpProvider('https://api.trongrid.io')\nsolidity_"
  },
  {
    "path": "requirements-docs.txt",
    "chars": 8,
    "preview": ".[docs]\n"
  },
  {
    "path": "setup.py",
    "chars": 3185,
    "preview": "#!/usr/bin/env python\n# --------------------------------------------------------------------\n# Copyright (c) iEXBase. Al"
  },
  {
    "path": "tronapi/__init__.py",
    "chars": 712,
    "preview": "# --------------------------------------------------------------------\n# Copyright (c) iEXBase. All rights reserved.\n# L"
  },
  {
    "path": "tronapi/common/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tronapi/common/abi.py",
    "chars": 15063,
    "preview": "# --------------------------------------------------------------------\n# Copyright (c) iEXBase. All rights reserved.\n# L"
  },
  {
    "path": "tronapi/common/account.py",
    "chars": 3192,
    "preview": "# --------------------------------------------------------------------\n# Copyright (c) iEXBase. All rights reserved.\n# L"
  },
  {
    "path": "tronapi/common/blocks.py",
    "chars": 1328,
    "preview": "# --------------------------------------------------------------------------------------------\n# Copyright (c) iEXBase. "
  },
  {
    "path": "tronapi/common/contracts.py",
    "chars": 4862,
    "preview": "# --------------------------------------------------------------------\n# Copyright (c) iEXBase. All rights reserved.\n# L"
  },
  {
    "path": "tronapi/common/datastructures.py",
    "chars": 6408,
    "preview": "from collections import (\n    Hashable,\n    Mapping,\n    MutableMapping,\n    OrderedDict,\n    Sequence,\n)\n\nfrom trx_util"
  },
  {
    "path": "tronapi/common/datatypes.py",
    "chars": 1520,
    "preview": "# --------------------------------------------------------------------\n# Copyright (c) iEXBase. All rights reserved.\n# L"
  },
  {
    "path": "tronapi/common/encoding.py",
    "chars": 7948,
    "preview": "import json\nimport re\nfrom typing import Union\n\nfrom eth_account.datastructures import AttributeDict\nfrom hexbytes impor"
  },
  {
    "path": "tronapi/common/formatters.py",
    "chars": 1781,
    "preview": "# --------------------------------------------------------------------\n# Copyright (c) iEXBase. All rights reserved.\n# L"
  },
  {
    "path": "tronapi/common/normalizers.py",
    "chars": 1820,
    "preview": "import functools\nimport json\n\nfrom eth_utils import (\n    is_binary_address,\n    to_hex,\n    hexstr_if_str\n)\nfrom hexbyt"
  },
  {
    "path": "tronapi/common/threads.py",
    "chars": 3280,
    "preview": "\"\"\"\nA minimal implementation of the various gevent APIs used within this codebase.\n\"\"\"\nimport threading\nimport time\n\n\ncl"
  },
  {
    "path": "tronapi/common/toolz/__init__.py",
    "chars": 1013,
    "preview": "# --------------------------------------------------------------------\n# Copyright (c) iEXBase. All rights reserved.\n# L"
  },
  {
    "path": "tronapi/common/transactions.py",
    "chars": 561,
    "preview": "from tronapi.common.threads import Timeout\n\n\ndef wait_for_transaction_id(tron, tx_id, timeout=120, poll_latency=0.1):\n  "
  },
  {
    "path": "tronapi/common/validation.py",
    "chars": 6614,
    "preview": "# --------------------------------------------------------------------------------------------\n# Copyright (c) iEXBase. "
  },
  {
    "path": "tronapi/constants.py",
    "chars": 582,
    "preview": "# --------------------------------------------------------------------\n# Copyright (c) iEXBase. All rights reserved.\n# L"
  },
  {
    "path": "tronapi/contract.py",
    "chars": 12561,
    "preview": "# --------------------------------------------------------------------\n# Copyright (c) iEXBase. All rights reserved.\n# L"
  },
  {
    "path": "tronapi/exceptions.py",
    "chars": 2329,
    "preview": "# --------------------------------------------------------------------\n# Copyright (c) iEXBase. All rights reserved.\n# L"
  },
  {
    "path": "tronapi/main.py",
    "chars": 11310,
    "preview": "# -*- coding: utf-8 -*-\n# --------------------------------------------------------------------\n# Copyright (c) iEXBase. "
  },
  {
    "path": "tronapi/manager.py",
    "chars": 4226,
    "preview": "# --------------------------------------------------------------------\n# Copyright (c) iEXBase. All rights reserved.\n# L"
  },
  {
    "path": "tronapi/module.py",
    "chars": 1009,
    "preview": "# --------------------------------------------------------------------\n# Copyright (c) iEXBase. All rights reserved.\n# L"
  },
  {
    "path": "tronapi/providers/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tronapi/providers/base.py",
    "chars": 1382,
    "preview": "# --------------------------------------------------------------------\n# Copyright (c) iEXBase. All rights reserved.\n# L"
  },
  {
    "path": "tronapi/providers/http.py",
    "chars": 4660,
    "preview": "# --------------------------------------------------------------------\n# Copyright (c) iEXBase. All rights reserved.\n# L"
  },
  {
    "path": "tronapi/transactionbuilder.py",
    "chars": 38618,
    "preview": "# --------------------------------------------------------------------\n# Copyright (c) iEXBase. All rights reserved.\n# L"
  },
  {
    "path": "tronapi/trx.py",
    "chars": 31831,
    "preview": "# --------------------------------------------------------------------\n# Copyright (c) iEXBase. All rights reserved.\n# L"
  }
]

About this extraction

This page contains the full source code of the iexbase/tron-api-python GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 50 files (190.1 KB), approximately 44.1k tokens, and a symbol index with 335 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!