Module tronpytool.trx

tronpytool.trx

Work with basic methods

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

Expand source code
#!/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.
# --------------------------------------------------------------------

"""
    tronpytool.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 tronpytool.common import key
from tronpytool.common.account import Account
from tronpytool.common.blocks import select_method_for_block
from tronpytool.common.toolz import (
    assoc
)
from tronpytool.common.transactions import wait_for_transaction_id
from tronpytool.contract import Contract
from tronpytool.exceptions import InvalidTronError, TronError, TimeExhausted, AssetNotFound
from tronpytool.module import Module

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
        return 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 provider')

        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, private_key=None, use_tron: bool = True, multisig: bool = False,
             is_message_hex=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))

            if is_message_hex:
                from eth_hash.auto import keccak as keccak_256
                message_hash = keccak_256(header.encode('utf-8') + bytes.fromhex(transaction))
            else:
                message_hash = self.tron.keccak(text=header + transaction)

            signed_message = Account.sign_hash(self.tron.toHex(message_hash), private_key or self.tron.private_key)
            return signed_message

        if not multisig and 'signature' in transaction:
            raise TronError('Transaction is already signed')

        try:
            if transaction.get('transaction', ''):
                transaction = transaction['transaction']

            if not multisig:
                address = self.tron.address.from_private_key(private_key or 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'], private_key or 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, is_message_hex=False):
        """ 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))

        if is_message_hex:
            from eth_hash.auto import keccak as keccak_256
            message_hash = keccak_256(header.encode('utf-8') + bytes.fromhex(message))
        else:
            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 get_asset_from_name(self, name: str) -> dict:
        """Get asset info from its abbr name, might fail if there're duplicates."""
        assets = [asset for asset in self.list_assets(1000, 0) if asset['abbr'] == name]
        if assets:
            if len(assets) == 1:
                return assets[0]
            raise ValueError("duplicated assets with the same name", [asset['id'] for asset in assets])
        raise AssetNotFound

    def list_assets(self, limit=0, offset=0) -> list:
        """List all TRC10 tokens(assets)."""
        assets = self.list_tokens(limit, offset)
        for asset in assets:
            asset["id"] = int(asset["id"])
            asset["owner_address"] = key.to_base58check_address(asset["owner_address"])
            asset["name"] = bytes.fromhex(asset["name"]).decode()
            if "abbr" in asset:
                asset["abbr"] = bytes.fromhex(asset["abbr"]).decode()
            else:
                asset["abbr"] = ""
            asset["description"] = bytes.fromhex(asset["description"]).decode("utf8", "replace")
            asset["url"] = bytes.fromhex(asset["url"]).decode()
        return assets

    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. JSON

        """

        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.manager.request('/wallet/listexchangespaginated', {
            'limit': limit,
            'offset': offset
        })

    def get_node_info(self):
        """Get info about thre node"""
        return self.tron.manager.request('/wallet/getnodeinfo')

    def get_token_list_name(self, token_id: str) -> any:
        """Query token list by name.

            Args:
                token_id (str): The name of the token
        """
        if not is_string(token_id):
            raise ValueError('Invalid token ID provided')

        return self.tron.manager.request('/wallet/getassetissuelistbyname', {
            'value': self.tron.toHex(text=token_id)
        })

    def get_token_by_id(self, token_id: str) -> any:
        """Query token by id.

            Args:
                token_id (str): The id of the token, it's a string
        """
        if not is_string(token_id):
            raise ValueError('Invalid token ID provided')

        return self.tron.manager.request('/wallet/getassetissuebyid', {
            'value': token_id
        })

Classes

class Trx (tron)

Module Class

Expand source code
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
        return 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 provider')

        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, private_key=None, use_tron: bool = True, multisig: bool = False,
             is_message_hex=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))

            if is_message_hex:
                from eth_hash.auto import keccak as keccak_256
                message_hash = keccak_256(header.encode('utf-8') + bytes.fromhex(transaction))
            else:
                message_hash = self.tron.keccak(text=header + transaction)

            signed_message = Account.sign_hash(self.tron.toHex(message_hash), private_key or self.tron.private_key)
            return signed_message

        if not multisig and 'signature' in transaction:
            raise TronError('Transaction is already signed')

        try:
            if transaction.get('transaction', ''):
                transaction = transaction['transaction']

            if not multisig:
                address = self.tron.address.from_private_key(private_key or 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'], private_key or 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, is_message_hex=False):
        """ 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))

        if is_message_hex:
            from eth_hash.auto import keccak as keccak_256
            message_hash = keccak_256(header.encode('utf-8') + bytes.fromhex(message))
        else:
            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 get_asset_from_name(self, name: str) -> dict:
        """Get asset info from its abbr name, might fail if there're duplicates."""
        assets = [asset for asset in self.list_assets(1000, 0) if asset['abbr'] == name]
        if assets:
            if len(assets) == 1:
                return assets[0]
            raise ValueError("duplicated assets with the same name", [asset['id'] for asset in assets])
        raise AssetNotFound

    def list_assets(self, limit=0, offset=0) -> list:
        """List all TRC10 tokens(assets)."""
        assets = self.list_tokens(limit, offset)
        for asset in assets:
            asset["id"] = int(asset["id"])
            asset["owner_address"] = key.to_base58check_address(asset["owner_address"])
            asset["name"] = bytes.fromhex(asset["name"]).decode()
            if "abbr" in asset:
                asset["abbr"] = bytes.fromhex(asset["abbr"]).decode()
            else:
                asset["abbr"] = ""
            asset["description"] = bytes.fromhex(asset["description"]).decode("utf8", "replace")
            asset["url"] = bytes.fromhex(asset["url"]).decode()
        return assets

    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. JSON

        """

        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.manager.request('/wallet/listexchangespaginated', {
            'limit': limit,
            'offset': offset
        })

    def get_node_info(self):
        """Get info about thre node"""
        return self.tron.manager.request('/wallet/getnodeinfo')

    def get_token_list_name(self, token_id: str) -> any:
        """Query token list by name.

            Args:
                token_id (str): The name of the token
        """
        if not is_string(token_id):
            raise ValueError('Invalid token ID provided')

        return self.tron.manager.request('/wallet/getassetissuelistbyname', {
            'value': self.tron.toHex(text=token_id)
        })

    def get_token_by_id(self, token_id: str) -> any:
        """Query token by id.

            Args:
                token_id (str): The id of the token, it's a string
        """
        if not is_string(token_id):
            raise ValueError('Invalid token ID provided')

        return self.tron.manager.request('/wallet/getassetissuebyid', {
            'value': token_id
        })

Ancestors

Class variables

var default_contract_factory

Methods

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
Expand source code
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 broadcast(self, signed_transaction)

Broadcast the signed transaction

Args

signed_transaction : object
signed transaction contract data
Expand source code
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 contract(self, address=None, **kwargs)

Work with a contract

Args

address : str
TRON Address
**kwargs : any
details (bytecode, abi)
Expand source code
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 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
Expand source code
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 get_account(self, address=None)

Query information about an account

Args

address : str
Address
Expand source code
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_account_by_id(self, account_id: str, options: object)
Expand source code
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)
Expand source code
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_account_resource(self, address=None)

Query the resource information of the account

Args

address : str
Address

Results

Resource information of the account

Expand source code
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_asset_from_name(self, name: str) ‑> dict

Get asset info from its abbr name, might fail if there're duplicates.

Expand source code
def get_asset_from_name(self, name: str) -> dict:
    """Get asset info from its abbr name, might fail if there're duplicates."""
    assets = [asset for asset in self.list_assets(1000, 0) if asset['abbr'] == name]
    if assets:
        if len(assets) == 1:
            return assets[0]
        raise ValueError("duplicated assets with the same name", [asset['id'] for asset in assets])
    raise AssetNotFound
def get_balance(self, address=None, is_float=False)

Getting a balance

Args

address : str
Address
is_float : bool
Convert to float format
Expand source code
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_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 }

Expand source code
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_block(self, block: Any = None)

Get block details using HashString or blockNumber

Args

block : Any
ID or height for the block
Expand source code
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
    return ValueError("The call to {method['url']} did not return a value.")
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
Expand source code
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_block_transaction_count(self, block: Any)

Total number of transactions in a block

Args

block : Any
Number or Hash Block
Expand source code
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_chain_parameters(self)

Getting chain parameters

Expand source code
def get_chain_parameters(self):
    """Getting chain parameters"""
    return self.tron.manager.request('/wallet/getchainparameters')
def get_confirmed_current_block(self)

Query the confirmed latest block

Expand source code
def get_confirmed_current_block(self):
    """Query the confirmed latest block"""
    return self.tron.manager.request('/walletsolidity/getnowblock')
def get_contract(self, contract_address)

Queries a contract's information from the blockchain.

Args

contract_address : str
contract address

Returns

SmartContract object. JSON

Expand source code
def get_contract(self, contract_address):
    """Queries a contract's information from the blockchain.

    Args:
        contract_address (str): contract address

    Returns:
        SmartContract object. JSON

    """

    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 get_current_block(self)

Query the latest block

Expand source code
def get_current_block(self):
    """Query the latest block"""
    return self.tron.manager.request(url='/wallet/getnowblock')
def get_exchange_by_id(self, exchange_id)

Find exchange by id

Args

exchange_id : str
ID Exchange
Expand source code
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_latest_blocks(self, num=1)

Query the latest blocks

Args

num : int
the number of blocks to query
Expand source code
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 get_list_exchangers(self)

Get list exchangers

Expand source code
def get_list_exchangers(self):
    """Get list exchangers"""
    return self.tron.manager.request('/wallet/listexchanges')
def get_node_info(self)

Get info about thre node

Expand source code
def get_node_info(self):
    """Get info about thre node"""
    return self.tron.manager.request('/wallet/getnodeinfo')
def get_proposal(self, proposal_id)

Query proposal based on id

Args

proposal_id : int
ID
Expand source code
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 get_token_by_id(self, token_id: str) ‑> 

Query token by id.

Args

token_id : str
The id of the token, it's a string
Expand source code
def get_token_by_id(self, token_id: str) -> any:
    """Query token by id.

        Args:
            token_id (str): The id of the token, it's a string
    """
    if not is_string(token_id):
        raise ValueError('Invalid token ID provided')

    return self.tron.manager.request('/wallet/getassetissuebyid', {
        'value': token_id
    })
def get_token_from_id(self, token_id: str)

Query token by name.

Args

token_id : str
The name of the token
Expand source code
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_token_list_name(self, token_id: str) ‑> 

Query token list by name.

Args

token_id : str
The name of the token
Expand source code
def get_token_list_name(self, token_id: str) -> any:
    """Query token list by name.

        Args:
            token_id (str): The name of the token
    """
    if not is_string(token_id):
        raise ValueError('Invalid token ID provided')

    return self.tron.manager.request('/wallet/getassetissuelistbyname', {
        'value': self.tron.toHex(text=token_id)
    })
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.

Expand source code
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_transaction(self, transaction_id: str, is_confirm: bool = False)

Query transaction based on id

Args

transaction_id : str
transaction id

is_confirm (bool):

Expand source code
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_transaction_count(self)

Count all transactions on the network Note: Possible delays

Returns

Total number of transactions.

Expand source code
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 get_transaction_count_by_blocknum(self, num: int)

Query transaction's count on a specified block by height

Args

num : int
block number
Expand source code
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 provider')

    return self.tron.manager.request('/wallet/gettransactioncountbyblocknum', {
        'num': num
    })
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

Expand source code
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 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

Expand source code
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_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

Expand source code
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)

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
Expand source code
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

Expand source code
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_unconfirmed_account_by_id(self, account_id: str)
Expand source code
def get_unconfirmed_account_by_id(self, account_id: str):

    return self.get_account_info_by_id(account_id, {
        'confirmed': True
    })
def list_assets(self, limit=0, offset=0) ‑> list

List all TRC10 tokens(assets).

Expand source code
def list_assets(self, limit=0, offset=0) -> list:
    """List all TRC10 tokens(assets)."""
    assets = self.list_tokens(limit, offset)
    for asset in assets:
        asset["id"] = int(asset["id"])
        asset["owner_address"] = key.to_base58check_address(asset["owner_address"])
        asset["name"] = bytes.fromhex(asset["name"]).decode()
        if "abbr" in asset:
            asset["abbr"] = bytes.fromhex(asset["abbr"]).decode()
        else:
            asset["abbr"] = ""
        asset["description"] = bytes.fromhex(asset["description"]).decode("utf8", "replace")
        asset["url"] = bytes.fromhex(asset["url"]).decode()
    return assets
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
Expand source code
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.manager.request('/wallet/listexchangespaginated', {
        'limit': limit,
        'offset': offset
    })
def list_nodes(self)

List the nodes which the api fullnode is connecting on the network

Expand source code
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 list_proposals(self)

Query all proposals

Returns

Proposal list information

Expand source code
def list_proposals(self):
    """Query all proposals

    Returns:
        Proposal list information

    """
    return self.tron.manager.request('/wallet/listproposals')
def list_super_representatives(self)

Query the list of Super Representatives

Expand source code
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

Expand source code
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 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
Expand source code
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 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

Expand source code
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 send(self, to, amount, options=None)

Send funds to the Tron account (option 2)

Expand source code
def send(self, to, amount, options=None):
    """Send funds to the Tron account (option 2)"""
    return self.send_transaction(to, amount, options)
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

Expand source code
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 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
Expand source code
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_trx(self, to, amount, options=None)

Send funds to the Tron account (option 3)

Expand source code
def send_trx(self, to, amount, options=None):
    """Send funds to the Tron account (option 3)"""
    return self.send_transaction(to, amount, options)
def sign(self, transaction: Any, private_key=None, use_tron: bool = True, multisig: bool = False, is_message_hex=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
Expand source code
def sign(self, transaction: Any, private_key=None, use_tron: bool = True, multisig: bool = False,
         is_message_hex=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))

        if is_message_hex:
            from eth_hash.auto import keccak as keccak_256
            message_hash = keccak_256(header.encode('utf-8') + bytes.fromhex(transaction))
        else:
            message_hash = self.tron.keccak(text=header + transaction)

        signed_message = Account.sign_hash(self.tron.toHex(message_hash), private_key or self.tron.private_key)
        return signed_message

    if not multisig and 'signature' in transaction:
        raise TronError('Transaction is already signed')

    try:
        if transaction.get('transaction', ''):
            transaction = transaction['transaction']

        if not multisig:
            address = self.tron.address.from_private_key(private_key or 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'], private_key or 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 sign_and_broadcast(self, transaction: Any)

Sign and send to the network

Args

transaction : Any
transaction details
Expand source code
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 time_until_next_vote_cycle(self)

Get the time of the next Super Representative vote

Returns

Number of milliseconds until the next voting time.

Expand source code
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 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
Expand source code
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 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
Expand source code
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 validate_address(self, address)

Validate address

Args

address : str
The address, should be in base58checksum
_is_hex : bool
hexString or base64 format
Expand source code
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 verify_message(self, message, signed_message=None, address=None, use_tron: bool = True, is_message_hex=False)

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
Expand source code
def verify_message(self, message, signed_message=None, address=None, use_tron: bool = True, is_message_hex=False):
    """ 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))

    if is_message_hex:
        from eth_hash.auto import keccak as keccak_256
        message_hash = keccak_256(header.encode('utf-8') + bytes.fromhex(message))
    else:
        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 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

Expand source code
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 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
Expand source code
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,
            )
        )