def transfer( self, destination_address, amount, asset="XLM", locked_until=None, memo_text=None, memo_hash=None, fund_transaction=True, from_address=None, ): """Transfer assets to another address :param destination_address: address of the destination. :type destination_address: str :param amount: amount, can be a floating point number with 7 numbers after the decimal point expressed as a Decimal or a string. :type amount: Union[str, Decimal] :param asset: asset to transfer (if none is specified the default 'XLM' is used), if you wish to specify an asset it should be in format 'assetcode:issuer'. Where issuer is the address of the issuer of the asset. :type asset: str :param locked_until: optional epoch timestamp indicating until when the tokens should be locked. :type locked_until: float :param text_memo: optional memo text to add to the transaction, a string encoded using either ASCII or UTF-8, up to 28-bytes long :type: Union[str, bytes] :param memo_hash: optional memo hash to add to the transaction, A 32 byte hash :type: Union[str, bytes] :param fund_transaction: use the threefoldfoundation transaction funding service :type: fund_transaction: bool :param from_address: Use a different address to send the tokens from, useful in multisig use cases. :type from_address: str """ issuer = None self._log_info(f"Sending {amount} {asset} to {destination_address}") if asset != "XLM": assetStr = asset.split(":") if len(assetStr) != 2: raise Exception("Wrong asset format") asset = assetStr[0] issuer = assetStr[1] if locked_until is not None: return self._transfer_locked_tokens( destination_address, amount, asset, issuer, locked_until, memo_text=memo_text, memo_hash=memo_hash, fund_transaction=fund_transaction, from_address=from_address) horizon_server = self._get_horizon_server() base_fee = horizon_server.fetch_base_fee() if from_address: source_account = horizon_server.load_account(from_address) else: source_account = self.load_account() transaction_builder = TransactionBuilder( source_account=source_account, network_passphrase=_NETWORK_PASSPHRASES[str(self.network)], base_fee=base_fee) transaction_builder.append_payment_op( destination=destination_address, amount=amount, asset_code=asset, asset_issuer=issuer, source=source_account.account_id, ) transaction_builder.set_timeout(30) if memo_text is not None: transaction_builder.add_text_memo(memo_text) if memo_hash is not None: transaction_builder.add_hash_memo(memo_hash) transaction = transaction_builder.build() transaction = transaction.to_xdr() if asset in _NETWORK_KNOWN_TRUSTS[str(self.network)]: if fund_transaction: transaction = self._fund_transaction(transaction=transaction) transaction = transaction["transaction_xdr"] transaction = TransactionEnvelope.from_xdr( transaction, _NETWORK_PASSPHRASES[str(self.network)]) my_keypair = Keypair.from_secret(self.secret) transaction.sign(my_keypair) try: response = horizon_server.submit_transaction(transaction) tx_hash = response["hash"] self._log_info("Transaction hash: {}".format(tx_hash)) return tx_hash except BadRequestError as e: result_codes = e.extras.get("result_codes") operations = result_codes.get("operations") if operations is not None: for op in operations: if op == "op_underfunded": raise e # if op_bad_auth is returned then we assume the transaction needs more signatures # so we return the transaction as xdr elif op == "op_bad_auth": self._log_info( "Transaction might need additional signatures in order to send" ) return transaction.to_xdr() raise e
def transfer(self, destination_address, amount, asset="XLM", locked_until=None, memo_text=None, memo_hash=None): """Transfer assets to another address :param destination_address: address of the destination. :type destination_address: str :param amount: amount, can be a floating point number with 7 numbers after the decimal point expressed as a string. :type amount: str :param asset: asset to transfer (if none is specified the default 'XLM' is used), if you wish to specify an asset it should be in format 'assetcode:issuer'. Where issuer is the address of the issuer of the asset. :type asset: str :param locked_until: optional epoch timestamp indicating until when the tokens should be locked. :type locked_until: float :param text_memo: optional memo text to add to the transaction, a string encoded using either ASCII or UTF-8, up to 28-bytes long :type: Union[str, bytes] :param memo_hash: optional memo hash to add to the transaction, A 32 byte hash :type: Union[str, bytes] """ issuer = None self._log_info("Sending {} {} to {}".format(amount, asset, destination_address)) if asset != "XLM": assetStr = asset.split(":") if len(assetStr) != 2: raise Exception("Wrong asset format") asset = assetStr[0] issuer = assetStr[1] if locked_until is not None: return self._transfer_locked_tokens(destination_address, amount, asset, issuer, locked_until) server = self._get_horizon_server() source_keypair = Keypair.from_secret(self.secret) source_public_key = source_keypair.public_key source_account = server.load_account(source_public_key) base_fee = server.fetch_base_fee() transaction_builder = TransactionBuilder( source_account=source_account, network_passphrase=_NETWORK_PASSPHRASES[str(self.network)], base_fee=base_fee) transaction_builder.append_payment_op(destination=destination_address, amount=str(amount), asset_code=asset, asset_issuer=issuer) transaction_builder.set_timeout(30) if memo_text is not None: transaction_builder.add_text_memo(memo_text) if memo_hash is not None: transaction_builder.add_hash_memo(memo_hash) transaction = transaction_builder.build() transaction.sign(source_keypair) try: response = server.submit_transaction(transaction) tx_hash = response["hash"] self._log_info("Transaction hash: {}".format(tx_hash)) return tx_hash except BadRequestError as e: self._log_debug(e) raise e