Esempio n. 1
0
 def transfer_funds(self, account: Account,
                    recipient_id: str,
                    percentage: float,
                    payload: str = "",
                    tx_ttl: int = defaults.TX_TTL,
                    include_fee=True):
     """
     Create and execute a spend transaction
     """
     if percentage < 0 or percentage > 1:
         raise ValueError(f"Percentage should be a number between 0 and 1, got {percentage}")
     account_on_chain = self.get_account_by_pubkey(pubkey=account.get_address())
     request_transfer_amount = int(account_on_chain.balance * percentage)
     # retrieve the nonce
     account.nonce = account_on_chain.nonce + 1
     # retrieve ttl
     tx_ttl = self.compute_absolute_ttl(tx_ttl)
     # build the transaction
     tx = self.tx_builder.tx_spend(account.get_address(), recipient_id, request_transfer_amount, payload, defaults.FEE, tx_ttl.absolute_ttl, account.nonce)
     # if the request_transfer_amount should include the fee keep calculating the fee
     if include_fee:
         amount = request_transfer_amount
         while (amount + tx.data.fee) > request_transfer_amount:
             amount = request_transfer_amount - tx.data.fee
             tx = self.tx_builder.tx_spend(account.get_address(), recipient_id, amount, payload, defaults.FEE, tx_ttl.absolute_ttl, account.nonce)
     # execute the transaction
     tx = self.sign_transaction(account, tx.tx)
     # post the transaction
     self.broadcast_transaction(tx.tx, tx_hash=tx.hash)
     return tx
Esempio n. 2
0
 def account_basic_to_ga(self, account: Account, ga_contract: str, calldata: str,
                         auth_fun: str = defaults.GA_AUTH_FUNCTION,
                         fee: int = defaults.FEE,
                         tx_ttl: int = defaults.TX_TTL,
                         gas: int = defaults.CONTRACT_GAS,
                         gas_price: int = defaults.CONTRACT_GAS_PRICE):
     """
     Transform a POA (Plain Old Account) to a GA (Generalized Account)
     :param account: the account to transform
     :param contract: the compiled contract associated to the GA
     :param auth_fun: the name of the contract function to use for authorization (default: authorize)
     """
     # check the auth_fun name
     if auth_fun is None or len(auth_fun) == 0:
         raise TypeError("The parameter auth_fun is required")
     # decode the contract and search for the authorization function
     auth_fun_hash = None
     contract_data = contract.CompilerClient.decode_bytecode(ga_contract)
     if len(contract_data.type_info) == 0:
         # TODO: we assume is a FATE env, but is a bit weak
         auth_fun_hash = hashing.hash(auth_fun.encode('utf-8'))
     for ti in contract_data.type_info:
         if ti.fun_name == auth_fun:
             auth_fun_hash = ti.fun_hash
     # if the function is not found then raise an error
     if auth_fun_hash is None:
         raise TypeError(f"Authorization function not found: '{auth_fun}'")
     # get the nonce
     nonce = self.get_next_nonce(account.get_address())
     # compute the ttl
     ttl = self.compute_absolute_ttl(tx_ttl).absolute_ttl
     # get abi and vm version
     vm_version, abi_version = self.get_vm_abi_versions()
     # build the transaction
     tx = self.tx_builder.tx_ga_attach(
         account.get_address(),
         nonce,
         ga_contract,
         auth_fun_hash,
         vm_version,
         abi_version,
         fee,
         ttl,
         gas,
         gas_price,
         calldata
     )
     # sign the transaction
     tx = self.sign_transaction(account, tx)
     # broadcast the transaction
     self.broadcast_transaction(tx)
     return tx
Esempio n. 3
0
    def sign_transaction(self,
                         account: Account,
                         tx: transactions.TxObject,
                         metadata: dict = {},
                         **kwargs) -> tuple:
        """
        Sign a transaction
        :return: the transaction for the transaction
        """
        # first retrieve the account from the node
        # so we can check if it is generalized or not
        on_chain_account = self.get_account(account.get_address())

        # if the account is not generalized sign and return the transaction
        if not on_chain_account.is_generalized():
            s = transactions.TxSigner(account, self.config.network_id)
            signature = s.sign_transaction(tx, metadata)
            return self.tx_builder.tx_signed([signature],
                                             tx,
                                             metadata=metadata)

        # if the account is generalized then prepare the ga_meta_tx
        # 1. wrap the tx into a signed tx (without signatures)
        sg_tx = self.tx_builder.tx_signed([], tx)
        # 2. wrap the tx into a ga_meta_tx
        # get the absolute ttl
        ttl = self.compute_absolute_ttl(kwargs.get(
            "ttl", defaults.TX_TTL)).absolute_ttl
        # get abi version
        _, abi = self.get_vm_abi_versions()
        # check that the parameter auth_data is provided
        auth_data = kwargs.get("auth_data")
        if auth_data is None:
            raise TypeError(
                "the auth_data parameter is required for ga accounts")
        # verify the gas amount TODO: add a tx verification
        gas = kwargs.get("gas", defaults.GA_MAX_AUTH_FUN_GAS)
        if gas > defaults.GA_MAX_AUTH_FUN_GAS:
            raise TypeError(
                f"the maximum gas value for ga auth_fun is {defaults.GA_MAX_AUTH_FUN_GAS}, got {gas}"
            )
        # build the
        ga_sg_tx = self.tx_builder.tx_ga_meta(
            account.get_address(), auth_data, kwargs.get("abi_version", abi),
            kwargs.get("fee", defaults.FEE), gas,
            kwargs.get("gas_price", defaults.CONTRACT_GAS_PRICE), ttl, sg_tx)
        # 3. wrap the the ga into a signed transaction
        sg_ga_sg_tx = self.tx_builder.tx_signed([],
                                                ga_sg_tx,
                                                metadata=metadata)
        return sg_ga_sg_tx
Esempio n. 4
0
 def spend(self, account: Account,
           recipient_id: str,
           amount: int,
           payload: str = "",
           fee: int = defaults.FEE,
           tx_ttl: int = defaults.TX_TTL):
     """
     Create and execute a spend transaction
     """
     # retrieve the nonce
     account.nonce = self.get_next_nonce(account.get_address()) if account.nonce == 0 else account.nonce + 1
     # retrieve ttl
     tx_ttl = self.compute_absolute_ttl(tx_ttl)
     # build the transaction
     tx = self.tx_builder.tx_spend(account.get_address(), recipient_id, amount, payload, fee, tx_ttl.absolute_ttl, account.nonce)
     # execute the transaction
     tx = self.sign_transaction(account, tx.tx)
     # post the transaction
     self.broadcast_transaction(tx.tx, tx_hash=tx.hash)
     return tx
Esempio n. 5
0
    def spend(self, account: Account,
              recipient_id: str,
              amount,
              payload: str = "",
              fee: int = defaults.FEE,
              tx_ttl: int = defaults.TX_TTL) -> transactions.TxObject:
        """
        Create and execute a spend transaction,
        automatically retrieve the nonce for the siging account
        and calculate the absolut ttl.

        :param account: the account signing the spend transaction (sender)
        :param recipient_id: the recipient address or name_id
        :param amount: the amount to spend
        :param payload: the payload for the transaction
        :param fee: the fee for the transaction (automatically calculated if not provided)
        :param tx_ttl: the transaction ttl expressed in relative number of blocks

        :return: the TxObject of the transaction

        :raises TypeError:  if the recipient_id is not a valid name_id or address

        """
        if utils.is_valid_aens_name(recipient_id):
            recipient_id = hashing.name_id(recipient_id)
        elif not utils.is_valid_hash(recipient_id, prefix="ak"):
            raise TypeError("Invalid recipient_id. Please provide a valid AENS name or account pub_key.")
        # parse amount and fee
        amount, fee = utils._amounts_to_aettos(amount, fee)
        # retrieve the nonce
        account.nonce = self.get_next_nonce(account.get_address()) if account.nonce == 0 else account.nonce + 1
        # retrieve ttl
        tx_ttl = self.compute_absolute_ttl(tx_ttl)
        # build the transaction
        tx = self.tx_builder.tx_spend(account.get_address(), recipient_id, amount, payload, fee, tx_ttl.absolute_ttl, account.nonce)
        # get the signature
        tx = self.sign_transaction(account, tx)
        # post the signed transaction transaction
        self.broadcast_transaction(tx)
        return tx
Esempio n. 6
0
 def transfer_funds(self, account: Account,
                    recipient_id: str,
                    percentage: float,
                    payload: str = "",
                    tx_ttl: int = defaults.TX_TTL,
                    fee: int = defaults.FEE,
                    include_fee=True):
     """
     Create and execute a spend transaction
     """
     if utils.is_valid_aens_name(recipient_id):
         recipient_id = hashing.name_id(recipient_id)
     elif not utils.is_valid_hash(recipient_id, prefix="ak"):
         raise TypeError("Invalid recipient_id. Please provide a valid AENS name or account pub_key.")
     if percentage < 0 or percentage > 1:
         raise ValueError(f"Percentage should be a number between 0 and 1, got {percentage}")
     # parse amounts
     fee = utils.amount_to_aettos(fee)
     # retrieve the balance
     account_on_chain = self.get_account_by_pubkey(pubkey=account.get_address())
     request_transfer_amount = int(account_on_chain.balance * percentage)
     # retrieve the nonce
     account.nonce = account_on_chain.nonce + 1
     # retrieve ttl
     tx_ttl = self.compute_absolute_ttl(tx_ttl)
     # build the transaction
     tx = self.tx_builder.tx_spend(account.get_address(), recipient_id, request_transfer_amount, payload, fee, tx_ttl.absolute_ttl, account.nonce)
     # if the request_transfer_amount should include the fee keep calculating the fee
     if include_fee:
         amount = request_transfer_amount
         while (amount + tx.data.fee) > request_transfer_amount:
             amount = request_transfer_amount - tx.data.fee
             tx = self.tx_builder.tx_spend(account.get_address(), recipient_id, amount, payload, fee, tx_ttl.absolute_ttl, account.nonce)
     # execute the transaction
     tx = self.sign_transaction(account, tx)
     # post the transaction
     self.broadcast_transaction(tx)
     return tx
Esempio n. 7
0
    def _delegate_common(self, account: Account, *kwargs):
        """
        Utility method to create a delegate signature for a contract

        Args:
            account: the account authorizing the transaction
            kwargs: the list of additional entity ids to be added to the data to be signed
        Returns:
            the signature to use for delegation
        """
        sig_data = self.config.network_id.encode("utf8") + hashing.decode(account.get_address())
        for _id in kwargs:
            sig_data += hashing.decode(_id)
        # sign the data
        sig = account.sign(sig_data)
        return sig
Esempio n. 8
0
    def sign_transaction(self, account: Account, tx: transactions.TxObject, metadata: dict = {}, **kwargs) -> transactions.TxObject:
        """
        The function sign a transaction to be broadcast to the chain.
        It automatically detect if the account is Basic or GA and return
        the correct transaction to be broadcast.

        :param account: the account signing the transaction
        :param tx: the transaction to be signed
        :param metadata: additional metadata to maintain in the TxObject
        :param `**kwargs`:  for GA accounts, see below

        Kwargs:
            :auth_data (str): the encoded calldata for the GA auth function
            :gas (int): the gas limit for the GA auth function [optional]
            :gas_price (int): the gas price for the GA auth function [optional]
            :fee (int): the fee for the GA transaction [optional, automatically calculated]
            :abi_version (int): the abi_version to select FATE or AEVM [optional, default 3/FATE]
            :ttl (str): the transaction ttl expressed in relative number of blocks [optional, default 0/max_ttl]:

        :return: a TxObject of the signed transaction or metat transaction for GA
        :raises TypeError: if the auth_data is missing and the account is GA
        :raises TypeError: if the gas for auth_func is gt defaults.GA_MAX_AUTH_FUN_GAS

        """
        # first retrieve the account from the node
        # so we can check if it is generalized or not
        on_chain_account = self.get_account(account.get_address())

        # if the account is not generalized sign and return the transaction
        if not on_chain_account.is_generalized():
            s = transactions.TxSigner(account, self.config.network_id)
            signature = s.sign_transaction(tx, metadata)
            return self.tx_builder.tx_signed([signature], tx, metadata=metadata)

        # if the account is generalized then prepare the ga_meta_tx
        # 1. wrap the tx into a signed tx (without signatures)
        sg_tx = self.tx_builder.tx_signed([], tx)
        # 2. wrap the tx into a ga_meta_tx
        # get the absolute ttl
        ttl = self.compute_absolute_ttl(kwargs.get("ttl", defaults.TX_TTL)).absolute_ttl
        # get abi version
        _, abi = self.get_vm_abi_versions()
        # check that the parameter auth_data is provided
        auth_data = kwargs.get("auth_data")
        if auth_data is None:
            raise TypeError("the auth_data parameter is required for ga accounts")
        # verify the gas amount TODO: add a tx verification
        gas = kwargs.get("gas", defaults.GA_MAX_AUTH_FUN_GAS)
        if gas > defaults.GA_MAX_AUTH_FUN_GAS:
            raise TypeError(f"the maximum gas value for ga auth_fun is {defaults.GA_MAX_AUTH_FUN_GAS}, got {gas}")
        # build the
        ga_sg_tx = self.tx_builder.tx_ga_meta(
            account.get_address(),
            auth_data,
            kwargs.get("abi_version", abi),
            kwargs.get("fee", defaults.FEE),
            gas,
            kwargs.get("gas_price", defaults.CONTRACT_GAS_PRICE),
            ttl,
            sg_tx
        )
        # 3. wrap the the ga into a signed transaction
        sg_ga_sg_tx = self.tx_builder.tx_signed([], ga_sg_tx, metadata=metadata)
        return sg_ga_sg_tx