def _merge_account(self, address): server = self._get_horizon_server() account = server.load_account(address) # Increment the sequence number in case the unlock transaction was not processed before the load_account call # account.increment_sequence_number() balances = self._get_free_balances(address) base_fee = server.fetch_base_fee() transaction_builder = TransactionBuilder( source_account=account, network_passphrase=_NETWORK_PASSPHRASES[str(self.network)], base_fee=base_fee) for balance in balances.balances: if balance.is_native(): continue # Step 1: Transfer custom assets transaction_builder.append_payment_op( destination=self.address, amount=balance.balance, asset_code=balance.asset_code, asset_issuer=balance.asset_issuer, ) # Step 2: Delete trustlines transaction_builder.append_change_trust_op( asset_issuer=balance.asset_issuer, asset_code=balance.asset_code, limit="0") # Step 3: Merge account transaction_builder.append_account_merge_op(self.address) transaction_builder.set_timeout(30) transaction = transaction_builder.build() signer_kp = Keypair.from_secret(self.secret) transaction.sign(signer_kp) server.submit_transaction(transaction)
def modify_signing_requirements(self, public_keys_signers, signature_count, low_treshold=0, high_treshold=2, master_weight=2): """modify_signing_requirements sets the signing requirements for a multisig account. It also adds the public keys of the signer to this account. :param public_keys_signers: list of public keys of signers. :type public_keys_signers: list :param signature_count: amount of signatures required to transfer funds. :type signature_count: int :param low_treshold: weight required for low security operations (transaction processing, allow trust, bump sequence) :type low_treshold: int :param high_treshold: weight required for high security operations (set options, account merge) :type high_treshold: int :param master_weight: A number from 0-255 (inclusive) representing the weight of the master key. If the weight of the master key is updated to 0, it is effectively disabled. :type master_weight: int """ account = self.load_account() source_keypair = Keypair.from_secret(self.secret) horizon_server = self._get_horizon_server() base_fee = horizon_server.fetch_base_fee() transaction_builder = TransactionBuilder( account, network_passphrase=_NETWORK_PASSPHRASES[str(self.network)], base_fee=base_fee) # set the signing options transaction_builder.append_set_options_op( low_threshold=low_treshold, med_threshold=signature_count, high_threshold=high_treshold, master_weight=master_weight) # For every public key given, add it as a signer to this account for public_key_signer in public_keys_signers: transaction_builder.append_ed25519_public_key_signer( public_key_signer, 1) transaction_builder.set_timeout(30) tx = transaction_builder.build() tx.sign(source_keypair) try: response = horizon_server.submit_transaction(tx) self._log_info(response) self._log_info( "Set the signers of {address} to require {signature_count} signers" .format(address=self.address, signature_count=signature_count)) except BadRequestError: self._log_info( "Transaction need additional signatures in order to send") return tx.to_xdr()
def modify_signing_requirements(self, public_keys_signers, signature_count, low_treshold=1, high_treshold=2): """modify_signing_requirements sets to amount of signatures required for the creation of multisig account. It also adds the public keys of the signer to this account :param public_keys_signers: list of public keys of signers. :type public_keys_signers: list :param signature_count: amount of signatures requires to transfer funds. :type signature_count: str :param low_treshold: amount of signatures required for low security operations (transaction processing, allow trust, bump sequence) :type low_treshold: str :param high_treshold: amount of signatures required for high security operations (set options, account merge) :type: str """ if len(public_keys_signers) != signature_count - 1: raise Exception( "Number of public_keys must be 1 less than the signature count in order to set the signature count" ) account = self.load_account() source_keypair = Keypair.from_secret(self.secret) transaction_builder = TransactionBuilder(account) # set the signing options transaction_builder.append_set_options_op( low_threshold=low_treshold, med_threshold=signature_count, high_threshold=high_treshold ) # For every public key given, add it as a signer to this account for public_key_signer in public_keys_signers: transaction_builder.append_ed25519_public_key_signer(public_key_signer, 1) transaction_builder.set_timeout(30) tx = transaction_builder.build() tx.sign(source_keypair) horizon_server = self._get_horizon_server() try: response = horizon_server.submit_transaction(tx) self._log_info(response) self._log_info( "Set the signers of {address} to require {signature_count} signers".format( address=self.address, signature_count=signature_count ) ) except BadRequestError: self._log_info("Transaction need additional signatures in order to send") return tx.to_xdr()
def add_balance(self, code, amount, accounts, dest, src, issuer): tb = TransactionBuilder( self.account_from_json(accounts[src.public_key]), base_fee=settings.MAX_TRANSACTION_FEE_STROOPS or settings.HORIZON_SERVER.fetch_base_fee(), network_passphrase="Test SDF Network ; September 2015", ) balance = self.get_balance(code, accounts[dest.public_key]) if not balance: print(f"\nCreating {code} trustline for {dest.public_key}") if settings.MAX_TRANSACTION_FEE_STROOPS: # halve the base_fee because there are 2 operations tb.base_fee = tb.base_fee // 2 tb.append_change_trust_op( asset_code=code, asset_issuer=issuer.public_key, source=dest.public_key ) payment_amount = amount elif Decimal(balance) < amount: print(f"\nReplenishing {code} balance to {amount} for {dest.public_key}") payment_amount = amount - Decimal(balance) else: print( "Destination account already has more than the amount " "specified, skipping\n" ) return print(f"Sending {code} payment of {payment_amount} to {dest.public_key}") tb.append_payment_op( destination=dest.public_key, amount=payment_amount, asset_code=code, asset_issuer=issuer.public_key, source=src.public_key, ) envelope = tb.set_timeout(30).build() if len(tb.operations) == 2: # add destination's signature if we're adding a trustline envelope.sign(dest) envelope.sign(src) try: self.server.submit_transaction(envelope) except BaseHorizonError as e: print( f"Failed to send {code} payment to {dest.public_key}. " f"Result codes: {e.extras.get('result_codes')}" ) else: print("Success!")
def stream_transaction_to_network(self, private_key: str, amount: str, tx_data: dict, dev_fee_status: bool = None): """ Place Transaction on the network """ key_pair = Keypair.from_secret(private_key) source_account = self.backoffice.stellar_wallet.server.load_account( key_pair.public_key) tx = TransactionBuilder( source_account=source_account, network_passphrase=Network.TESTNET_NETWORK_PASSPHRASE, base_fee=self.server.fetch_base_fee()).append_payment_op( destination=tx_data["toAddress"], asset_code="XLM", amount=tx_data["netValue"]).add_text_memo( memo_text=tx_data["memo"]) # Append Dev fee if selected if dev_fee_status: p = Payment(destination=self.backoffice.stellar_wallet.dev_key, asset=Asset.native(), amount=tx_data["devFee"]) tx.append_operation(operation=p) new_tx = tx.set_timeout(10).build() new_tx.sign(signer=private_key) try: # Sign Submit transaction to server result = self.server.submit_transaction(new_tx) return True, result except BadRequestError as e: print(f'Bad request {e}') return False, e except BadResponseError as e: print(f'Bad response {e}') return False, e except MemoInvalidException as e: print(f'Invalid memo {e}') return False, e except Exception as e: print(f'Else: {e}') return False, e
def add_balance(self, code, amount, accounts, dest, src, issuer): tb = TransactionBuilder( self.account_from_json(accounts[src.public_key]), base_fee=500, network_passphrase="Test SDF Network ; September 2015", ) payment_amount = None balance = self.get_balance(code, accounts[dest.public_key]) if not balance: print(f"\nCreating {code} trustline for {dest.public_key}") limit = amount if src == issuer else None tb.append_change_trust_op(code, issuer.public_key, limit, dest.public_key) payment_amount = amount elif Decimal(balance) < amount: print( f"\nReplenishing {code} balance to {amount} for {dest.public_key}" ) payment_amount = amount - Decimal(balance) else: print("Destination account already has more than the amount " "specified, skipping") return print( f"Sending {code} payment of {payment_amount} to {dest.public_key}") tb.append_payment_op(dest.public_key, payment_amount, code, issuer.public_key, src.public_key) envelope = tb.set_timeout(30).build() envelope.sign(src) if len(tb.operations) == 2: # We only need the destination's signature if we're adding a trustline # to the account envelope.sign(dest) try: self.server.submit_transaction(envelope) except BaseHorizonError as e: print(f"Failed to send {code} payment to {dest.public_key}. " f"Result codes: {e.extras.get('result_codes')}") else: print("Success!")
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