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

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

    raise TypeError(
        "The following abi value is not a '{abi_type}': {value}".format(
            abi_type=abi_type, value=value))
    def update_account(self, account_name, account):
        """Modify account name

        Note: Username is allowed to edit only once.

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

        Returns:
            modified Transaction Object

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

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

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

        return response
Example #3
0
    def to_hex(self, val):
        """
        Helper function that will convert a generic value to hex.

        Note: This function does not convert TRX addresses to Hex.
        If you wish to specifically convert TRX addresses to HEX,
        please use tron.address.to_hex instead.


        Example:
            >>> tron.to_hex("test")
            >>> # result "74657374"

        """
        if is_boolean(val):
            return self.from_decimal(+val)

        if type(val) == dict:
            return self.from_utf8(json.dumps(val).replace(' ', ''))

        if is_string(val):
            if val[:2] == '0x':
                return val

            if not isinstance(val, numbers.Real):
                return self.from_utf8(val)

        return self.from_decimal(val)
Example #4
0
    def __init__(self, tron, event_server):
        self.tron = tron

        if is_string(event_server):
            event_server = HttpProvider(event_server)

        self.event_server = event_server
Example #5
0
    def __init__(self,
                 full_node,
                 solidity_node,
                 event_server=None,
                 private_key=None):
        """Connect to the Tron network.

        Parameters:
            full_node (:obj:`str`): A provider connected to a valid full node
            solidity_node (:obj:`str`): A provider connected to a valid solidity node
            event_server (:obj:`str`, optional): Optional for smart contract events. Expects a valid event server URL
            private_key (str): Optional default private key used when signing transactions

        The idea is to have a class that allows to do this:

        .. code-block:: python
        >>> from tronapi.tron import Tron
        >>>
        >>> full_node = HttpProvider('https://api.trongrid.io')
        >>> solidity_node = HttpProvider('https://api.trongrid.io')
        >>> event_server = 'https://api.trongrid.io'
        >>>
        >>> tron = Tron()
        >>> print(tron.get_current_block())

         This class also deals with edits, votes and reading content.
        """

        # check received nodes
        if is_string(full_node):
            full_node = HttpProvider(full_node)
        if is_string(solidity_node):
            solidity_node = HttpProvider(solidity_node)

        # node setup
        self.__set_full_node(full_node)
        self.__set_solidity_node(solidity_node)

        self._default_block = None
        self._private_key = private_key
        self.default_address = Address(base58=None, hex=None)

        self._nodes = dict(full=self.full_node, solidity=self.solidity_node)

        self.events = Event(self, event_server)
        self.transaction = TransactionBuilder(self)
Example #6
0
def is_hex_encoded_block_number(value):
    if not is_string(value):
        return False
    elif is_hex_encoded_block_hash(value):
        return False
    try:
        value_as_int = int(value, 16)
    except ValueError:
        return False
    return 0 <= value_as_int < 2**256
Example #7
0
def map_collection(func, collection):
    """
    Apply func to each element of a collection, or value of a dictionary.
    If the value is not a collection, return it unmodified
    """
    datatype = type(collection)
    if isinstance(collection, Mapping):
        return datatype((key, func(val)) for key, val in collection.items())
    if is_string(collection):
        return collection
    elif isinstance(collection, Iterable):
        return datatype(map(func, collection))
    else:
        return collection
Example #8
0
    def send_token(self, to, amount, token_id, account=None):
        """Transfer Token

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

        Returns:
            Token transfer Transaction raw data

        """

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

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

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

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

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

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

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

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

        return self.tron.manager.request(
            '/wallet/transferasset', {
                'to_address': _to,
                'owner_address': _from,
                'asset_name': _token_id,
                'amount': amount
            })
Example #9
0
    def sign(self, transaction: Any, use_tron: bool = True):
        """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 (Any): transaction details
            use_tron (bool): is Tron header

        """

        if is_string(transaction):
            if not is_hex(transaction):
                raise TronError('Expected hex message input')

            # Determine which header to attach to the message
            # before encrypting or decrypting
            header = TRX_MESSAGE_HEADER if use_tron else ETH_MESSAGE_HEADER
            header += str(len(transaction))

            message_hash = self.tron.sha3(text=header + transaction)
            signed_message = Account.signHash(
                message_hash, private_key=self.tron.private_key)

            return signed_message

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

        # This option deals with signing of transactions, and writing to the array
        signed_tx = Account.signHash(message_hash=transaction['txID'],
                                     private_key=self.tron.private_key)
        transaction['signature'] = [signed_tx['signature'].hex()[2:]]

        return transaction
Example #10
0
    def sign(self, transaction: Any, use_tron: bool = True):
        """Sign the transaction, the api has the risk of leaking the private key,
        please make sure to call the api in a secure environment

        Args:
            transaction (Any): transaction details
            use_tron (bool): is Tron header

        """

        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
            message_hash = self.tron.sha3(text=header + transaction)
            signed_message = EthAccount.signHash(
                message_hash, private_key=self.tron.private_key)

            return signed_message

        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 send_token(self, to, amount, token_id, account):
        """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 not self.tron.isAddress(to):
            raise InvalidTronError('Invalid recipient address provided')

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

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

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

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

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

        return self.tron.manager.request(
            '/wallet/transferasset', {
                'to_address': _to,
                'owner_address': _from,
                'asset_name': _token_id,
                'amount': self.tron.toSun(amount)
            })
Example #12
0
    def __init__(self, tron, providers):
        """Create new manager tron instance

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

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

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

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

        Issuing a token on the TRON Protocol can be done by anyone
        who has at least 1024 TRX in their account.
        When a token is issued it will be shown on the token overview page.
        Users can then participate within the issuing time and exchange their
        TRX for tokens.After issuing the token your account will
        receive the amount of tokens equal to the total supply.
        When other users exchange their TRX for tokens then the tokens
        will be withdrawn from your account and you will receive
        TRX equal to the specified exchange rate.


        Args:
            **kwargs: Fill in the required parameters

        Examples:

            >>> start_func = datetime.now()
            >>> start = int(start_func.timestamp() * 1000)
            >>>
            >>> end_func = datetime.now() + timedelta(days=2)
            >>> end = int(end_func.timestamp() * 1000)
            >>>
            >>> opt = {
            >>>     'name': 'Tron',
            >>>     'abbreviation': 'TRX',
            >>>     'description': 'Hello World',
            >>>     'url': 'https://github.com',
            >>>     'totalSupply': 25000000,
            >>>     'frozenAmount': 1,
            >>>     'frozenDuration': 2,
            >>>     'freeBandwidth': 10000,
            >>>     'freeBandwidthLimit': 10000,
            >>>     'saleStart': start,
            >>>     'saleEnd': end,
            >>>     'voteScore': 1
            >>> }

        """
        issuer_address = kwargs.setdefault(
            'issuer_address', self.tron.default_address.hex
        )

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

        total_supply = kwargs.setdefault('totalSupply', 0)
        trx_ratio = kwargs.setdefault('trxRatio', 1)
        token_ratio = kwargs.setdefault('tokenRatio', 1)
        sale_start = kwargs.setdefault(
            'saleStart', START_DATE
        )
        free_bandwidth = kwargs.setdefault('freeBandwidth', 0)
        free_bandwidth_limit = kwargs.setdefault('freeBandwidthLimit', 0)
        frozen_amount = kwargs.setdefault('frozenAmount', 0)
        frozen_duration = kwargs.setdefault('frozenDuration', 0)
        vote_score = kwargs.setdefault('voteScore', 0)

        if not is_string(kwargs.get('name')):
            raise ValueError('Invalid token name provided')

        if not is_string(kwargs.get('abbreviation')):
            raise ValueError('Invalid token abbreviation provided')

        if not is_integer(total_supply) or total_supply <= 0:
            raise ValueError('Invalid supply amount provided')

        if not is_integer(trx_ratio) or trx_ratio <= 0:
            raise ValueError('TRX ratio must be a positive integer')

        if not is_integer(token_ratio) or token_ratio <= 0:
            raise ValueError('Token ratio must be a positive integer')

        if not is_integer(vote_score) or vote_score <= 0:
            raise ValueError('Invalid vote score provided')

        if not is_integer(sale_start) or sale_start < START_DATE:
            raise ValueError('Invalid sale start timestamp provided')

        if not is_integer(kwargs.get('saleEnd')) or \
                kwargs.get('saleEnd') <= sale_start:
            raise ValueError('Invalid sale end timestamp provided')

        if not is_string(kwargs.get('description')):
            raise ValueError('Invalid token description provided')

        if not is_valid_url(kwargs.get('url')):
            raise ValueError('Invalid token url provided')

        if not is_integer(free_bandwidth) or free_bandwidth < 0:
            raise ValueError('Invalid free bandwidth amount provided')

        if not is_integer(free_bandwidth_limit) or free_bandwidth_limit < 0 \
                or (free_bandwidth and not free_bandwidth_limit):
            raise ValueError('Invalid free bandwidth limit provided')

        if not is_integer(frozen_amount) or frozen_amount < 0 \
                or (not frozen_duration and frozen_amount):
            raise ValueError('Invalid frozen supply provided')

        if not is_integer(frozen_duration) or frozen_duration < 0 \
                or (frozen_duration and not frozen_amount):
            raise ValueError('Invalid frozen duration provided')

        frozen_supply = {
            'frozen_amount': int(frozen_amount),
            'frozen_days': int(frozen_duration)
        }

        response = self.tron.manager.request('/wallet/createassetissue', {
            'owner_address': self.tron.address.to_hex(issuer_address),
            'name': self.tron.toHex(text=kwargs.get('name')),
            'abbr': self.tron.toHex(text=kwargs.get('abbreviation')),
            'description': self.tron.toHex(text=kwargs.get('description')),
            'url': self.tron.toHex(text=kwargs.get('url')),
            'total_supply': int(total_supply),
            'trx_num': int(trx_ratio),
            'num': int(token_ratio),
            'start_time': int(sale_start),
            'end_time': int(kwargs.get('saleEnd')),
            'free_asset_net_limit': int(free_bandwidth),
            'public_free_asset_net_limit': int(free_bandwidth_limit),
            'frozen_supply': frozen_supply,
            'vote_score': vote_score
        })

        return response
    def trigger_smart_contract(self, contract_address,
                               function_selector,
                               fee_limit: int = 1000000000,
                               call_value: int = 0,
                               parameters=None,
                               issuer_address=None
                               ):
        """Trigger Smart Contract (Beta Version)
        Calls a function on a contract

        Args:
            contract_address (str): Contract address, converted to a hex string
            function_selector (str): Function signature. No spaces.
            fee_limit (int): Maximum TRX consumption, measured in SUN(1TRX = 1,000,000SUN)
            call_value (int): Amount of TRX transferred with this transaction, measured in SUN(1TRX = 1,000,000SUN)
            parameters (any): Call the virtual machine format of the parameter [1, 2],
                                use the js tool provided by remix, convert the parameter array [1, 2]
                                called by the contract caller into
                                the parameter format required by the virtual machine.

            issuer_address (str): address that is trigger the contract

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

        Returns:
            TransactionExtention, TransactionExtention contains unsigned Transaction
        """

        if parameters is None:
            parameters = []

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

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

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

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

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

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

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

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

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

        else:
            parameters = ''

        return self.tron.manager.request('/wallet/triggersmartcontract', {
            'contract_address': self.tron.address.to_hex(contract_address),
            'owner_address': self.tron.address.to_hex(issuer_address),
            'function_selector': function_selector,
            'fee_limit': int(fee_limit),
            'call_value': int(call_value),
            'parameter': parameters
        })
Example #15
0
def is_hex_encoded_block_hash(value):
    if not is_string(value):
        return False
    return len(remove_0x_prefix(value)) == 64 and is_hex(value)