def fund_trustline(self, address: str, asset: str) -> dict: wallet = self._get_wallet() if asset not in SUPPORTED_ASSETS[str(wallet.network.value)]: raise j.exceptions.NotFound("Unsupported asset") asset_code, asset_issuer = asset.split(":") server = wallet._get_horizon_server() source_account = wallet.load_account() base_fee = server.fetch_base_fee() transaction = (stellar_sdk.TransactionBuilder( source_account=source_account, network_passphrase=self._get_network_passphrase( wallet.network.value), base_fee=base_fee, ).append_begin_sponsoring_future_reserves_op( address).append_change_trust_op( asset_code=asset_code, asset_issuer=asset_issuer, source=address).append_end_sponsoring_future_reserves_op( address).set_timeout(60).build()) source_keypair = stellar_sdk.Keypair.from_secret(wallet.secret) transaction.sign(source_keypair) return {"addtrustline_transaction": transaction.to_xdr()}
def _create_recovery_transaction( self, vesting_address: str) -> stellar_sdk.TransactionEnvelope: activation_account_id = get_wallet().address horizon_server = self._get_horizon_server() source_account = horizon_server.load_account(vesting_address) source_account.sequence += 1 tx = stellar_sdk.TransactionBuilder( source_account, network_passphrase=self._get_network_passphrase()) tx.append_change_trust_op(asset_code="TFT", asset_issuer=self._get_tft_issuer(), limit="0") tx.append_manage_data_op(DATA_ENTRY_KEY, None) tx.append_account_merge_op(activation_account_id) txe = tx.build() # save the preauth transaction in our unlock service unlock_hash_signer = stellar_sdk.strkey.StrKey.encode_pre_auth_tx( txe.hash()) data = { "unlockhash": unlock_hash_signer, "transaction_xdr": txe.to_xdr() } url = f"https://{'testnet.threefold.io' if self._get_network()=='TEST' else'tokenservices.threefold.io'}/threefoldfoundation/unlock_service/create_unlockhash_transaction" resp = j.tools.http.post(url, json={"args": data}) resp.raise_for_status() return txe
def set_data_entry(self, name: str, value: str, address: str = None): """Sets, modifies or deletes a data entry (name/value pair) for an account To delete a data entry, set the value to an empty string. """ address = address or self.address signing_key = stellar_sdk.Keypair.from_secret(self.secret) horizon_server = self._get_horizon_server() if address == self.address: account = self.load_account() else: account = horizon_server.load_account(address) base_fee = horizon_server.fetch_base_fee() transaction = ( stellar_sdk.TransactionBuilder( source_account=account, network_passphrase=_NETWORK_PASSPHRASES[self.network.value], base_fee=base_fee, ) .append_manage_data_op(name, value) .set_timeout(30) .build() ) transaction.sign(signing_key) try: response = horizon_server.submit_transaction(transaction) j.logger.info("Transaction hash: {}".format(response["hash"])) except stellar_sdk.exceptions.BadRequestError as e: j.logger.debug(e) raise e
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 = stellar_sdk.TransactionBuilder( source_account=account, network_passphrase=_NETWORK_PASSPHRASES[self.network.value], 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 = stellar_sdk.Keypair.from_secret(self.secret) transaction.sign(signer_kp) server.submit_transaction(transaction)
def activate_account(self, destination_address, starting_balance="12.50"): """Activates another account Args: destination_address (str): address of the destination starting_balance (str, optional): the balance that the destination address will start with. Must be a positive integer expressed as a string. Defaults to "12.50". """ server = self._get_horizon_server() source_keypair = stellar_sdk.Keypair.from_secret(self.secret) source_account = self.load_account() base_fee = server.fetch_base_fee() transaction = ( stellar_sdk.TransactionBuilder( source_account=source_account, network_passphrase=_NETWORK_PASSPHRASES[self.network.value], base_fee=base_fee, ) .append_create_account_op(destination=destination_address, starting_balance=starting_balance) .build() ) transaction.sign(source_keypair) try: response = server.submit_transaction(transaction) j.logger.info("Transaction hash: {}".format(response["hash"])) except stellar_sdk.exceptions.BadRequestError as e: j.logger.debug(e)
def create_buy_order(buying_account_secret, amount, network): buying_asset_code = "TFT" buying_asset_issuer = ASSET_ISSUERS[network][buying_asset_code] selling_asset_code = "BTC" selling_asset_issuer = ASSET_ISSUERS[network][selling_asset_code] price = "0.0000019" buying_kp = Keypair.from_secret(buying_account_secret) horizon_server = stellar_sdk.Server(_HORIZON_NETWORKS[network]) source_account = horizon_server.load_account(buying_kp.public_key) transaction = ( stellar_sdk.TransactionBuilder( source_account=source_account, network_passphrase=stellar_sdk.Network.TESTNET_NETWORK_PASSPHRASE if network == "test" else stellar_sdk.Network.PUBLIC_NETWORK_PASSPHRASE, base_fee=0, ) .append_manage_buy_offer_op( selling_code=selling_asset_code, selling_issuer=selling_asset_issuer, buying_code=buying_asset_code, buying_issuer=buying_asset_issuer, amount=amount, price=price, ) .set_timeout(60) .build() ) transaction.sign(buying_kp) return transaction
def activate_account(source_keypair, new_account_kp, fullassetcode): split_asset = fullassetcode.split(":", 1) asset_code = split_asset[0] asset_issuer = split_asset[1] new_account_address = new_account_kp.public_key horizon_server = stellar_sdk.Server() source_account = horizon_server.load_account(source_keypair.public_key) base_fee = horizon_server.fetch_base_fee() transaction = (stellar_sdk.TransactionBuilder( source_account=source_account, network_passphrase=stellar_sdk.Network.TESTNET_NETWORK_PASSPHRASE, base_fee=base_fee, ).append_begin_sponsoring_future_reserves_op( new_account_address).append_create_account_op( destination=new_account_address, starting_balance="0").append_change_trust_op( asset_issuer=asset_issuer, asset_code=asset_code, source=new_account_address). append_end_sponsoring_future_reserves_op( new_account_address).set_timeout(60).build()) transaction.sign(source_keypair) transaction.sign(new_account_kp) horizon_server.submit_transaction(transaction)
def createpayment_transaction(accountsecret, destination, feedestination, fee, asset, amount, network): network_passphrase = (stellar_sdk.Network.TESTNET_NETWORK_PASSPHRASE if network == "test" else stellar_sdk.Network.PUBLIC_NETWORK_PASSPHRASE) horizon_server = stellar_sdk.Server( "https://horizon-testnet.stellar.org" if network == "test" else "https://horizon.stellar.org") keypair = Keypair.from_secret(accountsecret) from_address = keypair.public_key split_asset = asset.split(":", 1) asset_code = split_asset[0] asset_issuer = split_asset[1] source_account = horizon_server.load_account(from_address) txe = (stellar_sdk.TransactionBuilder( source_account=source_account, network_passphrase=network_passphrase, base_fee=0).append_payment_op( destination, amount, asset_code=asset_code, asset_issuer=asset_issuer).append_payment_op( feedestination, fee, asset_code=asset_code, asset_issuer=asset_issuer).set_timeout(60 * 3).build()) txe.sign(keypair) print(txe.to_xdr())
def _create_unlock_transaction(self, escrow_kp, unlock_time): server = self._get_horizon_server() escrow_account = server.load_account(escrow_kp.public_key) escrow_account.increment_sequence_number() tx = ( stellar_sdk.TransactionBuilder(escrow_account) .append_set_options_op(master_weight=0, low_threshold=1, med_threshold=1, high_threshold=1) .add_time_bounds(unlock_time, 0) .build() ) tx.sign(escrow_kp) return tx
def modify_signing_requirements(self, public_keys_signers, signature_count, low_treshold=0, high_treshold=2, master_weight=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 Args: public_keys_signers (list): list of public keys of signers signature_count (int): amount of signatures requires to transfer funds low_treshold (int, optional): amount of signatures required for low security operations (transaction processing, allow trust, bump sequence). Defaults to 1. high_treshold (int, optional): amount of signatures required for high security operations (set options, account merge). Defaults to 2. master_weight (int, optional): 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. Defaults to 2. """ server = self._get_horizon_server() account = self.load_account() source_keypair = stellar_sdk.Keypair.from_secret(self.secret) transaction_builder = stellar_sdk.TransactionBuilder(account) # 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 = server.submit_transaction(tx) j.logger.info(response) j.logger.info( f"Set the signers of {self.address} to require {signature_count} signers" ) except stellar_sdk.exceptions.BadRequestError: j.logger.info( "Transaction need additional signatures in order to send") return tx.to_xdr()
def end_sponsorship(activatorsecret, sponsoredaccount): activator_kp = Keypair.from_secret(activatorsecret) horizon_server = stellar_sdk.Server() source_account = horizon_server.load_account(activator_kp.public_key) base_fee = horizon_server.fetch_base_fee() transaction = (stellar_sdk.TransactionBuilder( source_account=source_account, network_passphrase=stellar_sdk.Network.TESTNET_NETWORK_PASSPHRASE, base_fee=base_fee, ).append_revoke_account_sponsorship_op(sponsoredaccount).build()) transaction.sign(activator_kp) horizon_server.submit_transaction(transaction) print(f"Stopped sponsoring account {sponsoredaccount }")
def merge_into_account(self, destination_address: str): """Merges XLMs into destination address Args: destination_address (str): address to send XLMs to """ server = self._get_horizon_server() source_keypair = stellar_sdk.Keypair.from_secret(self.secret) source_account = self.load_account() base_fee = server.fetch_base_fee() transaction_builder = stellar_sdk.TransactionBuilder( source_account=source_account, network_passphrase=_NETWORK_PASSPHRASES[self.network.value], base_fee=base_fee, ) balances = self.get_balance() for balance in balances.balances: if balance.is_native(): continue # Step 1: Transfer custom assets if decimal.Decimal(balance.balance) > decimal.Decimal(0): transaction_builder.append_payment_op( destination=destination_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") transaction_builder.append_account_merge_op( destination=destination_address) transaction = transaction_builder.build() transaction.sign(source_keypair) try: response = server.submit_transaction(transaction) j.logger.info("Transaction hash: {}".format(response["hash"])) except stellar_sdk.exceptions.BadRequestError as e: j.logger.debug(e)
def _set_account_signers(self, address, public_key_signer, preauth_tx_hash, signer_kp): server = self._get_horizon_server() if address == self.address: account = self.load_account() else: account = server.load_account(address) tx = ( stellar_sdk.TransactionBuilder(account) .append_pre_auth_tx_signer(preauth_tx_hash, 1) .append_ed25519_public_key_signer(public_key_signer, 1) .append_set_options_op(master_weight=1, low_threshold=2, med_threshold=2, high_threshold=2) .build() ) tx.sign(signer_kp) response = server.submit_transaction(tx) j.logger.info(response) j.logger.info(f"Set the signers of {address} to {public_key_signer} and {preauth_tx_hash}")
def remove_signer(self, public_key_signer): """remove_signer removes a public key as a signer from the source account Args: public_key_signer (str): public key of an account """ server = self._get_horizon_server() account = self.load_account() tx = stellar_sdk.TransactionBuilder(account).append_ed25519_public_key_signer(public_key_signer, 0).build() source_keypair = stellar_sdk.Keypair.from_secret(self.secret) tx.sign(source_keypair) try: response = server.submit_transaction(tx) j.logger.info(response) j.logger.info("Multisig tx signed and sent") except stellar_sdk.exceptions.BadRequestError: j.logger.info("Transaction need additional signatures in order to send") return tx.to_xdr()
def _change_trustline(self, asset_code, issuer, limit=None, secret=None): """Create a trustline between you and the issuer of an asset Args: asset_code (str): code which form the asset. For example: 'BTC', 'TFT', ... issuer (str): address of the asset issuer limit ([type], optional): The limit for the asset, defaults to max int64(922337203685.4775807). If the limit is set to “0” it deletes the trustline. Defaults to None. secret (str, optional): Secret to use will use instance property if empty. Defaults to None. """ # if no secret is provided we assume we change trustlines for this account secret = secret or self.secret server = self._get_horizon_server() source_keypair = stellar_sdk.Keypair.from_secret(secret) source_public_key = source_keypair.public_key source_account = server.load_account(source_public_key) base_fee = server.fetch_base_fee() transaction = ( stellar_sdk.TransactionBuilder( source_account=source_account, network_passphrase=_NETWORK_PASSPHRASES[self.network.value], base_fee=base_fee, ) .append_change_trust_op(asset_issuer=issuer, asset_code=asset_code, limit=limit) .set_timeout(30) .build() ) transaction.sign(source_keypair) try: server.submit_transaction(transaction) if limit is None: j.logger.info(f"Added trustline {asset_code}:{issuer} to account {self.address}") except stellar_sdk.exceptions.BadRequestError as e: j.logger.debug(e) raise e
def merge_into_account(self, destination_address: str): """Merges XLMs into destination address Args: destination_address (str): address to send XLMs to """ server = self._get_horizon_server() source_keypair = stellar_sdk.Keypair.from_secret(self.secret) source_account = self.load_account() base_fee = server.fetch_base_fee() transaction = (stellar_sdk.TransactionBuilder( source_account=source_account, network_passphrase=self.network["network_passphrase"], base_fee=base_fee, ).append_account_merge_op(destination=destination_address).build()) transaction.sign(source_keypair) try: response = server.submit_transaction(transaction) print("Transaction hash: {}".format(response["hash"])) except stellar_sdk.exceptions.BadRequestError as e: print(e)
def _activate_account(self, address): wallet = self._get_wallet() tftasset = wallet._get_asset() server = wallet._get_horizon_server() source_account = wallet.load_account() base_fee = server.fetch_base_fee() transaction = (stellar_sdk.TransactionBuilder( source_account=source_account, network_passphrase=self._get_network_passphrase( wallet.network.value), base_fee=base_fee, ).append_begin_sponsoring_future_reserves_op( address).append_create_account_op( destination=address, starting_balance="0").append_change_trust_op( asset_issuer=tftasset.issuer, asset_code=tftasset.code, source=address).append_end_sponsoring_future_reserves_op( address).set_timeout(60).build()) source_keypair = stellar_sdk.Keypair.from_secret(wallet.secret) transaction.sign(source_keypair) return transaction.to_xdr()
def transfer( self, destination_address, amount, asset="XLM", locked_until=None, memo_text=None, memo_hash=None, fund_transaction=True, from_address=None, timeout=30, sequence_number: int = None, sign: bool = True, ): """Transfer assets to another address Args: destination_address (str): address of the destination amount (str): can be a floating point number with 7 numbers after the decimal point expressed as a string asset (str, optional): asset to transfer. Defaults to "XLM". 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. locked_until (float, optional): epoch timestamp indicating until when the tokens should be locked. Defaults to None. memo_text (Union[str, bytes], optional): memo text to add to the transaction, a string encoded using either ASCII or UTF-8, up to 28-bytes long. Defaults to None. memo_hash (Union[str, bytes], optional): memo hash to add to the transaction, A 32 byte hash. Defaults to None. fund_transaction (bool, optional): use the threefoldfoundation transaction funding service. Defautls to True. from_address (str, optional): Use a different address to send the tokens from, useful in multisig use cases. Defaults to None. timeout (int,optional: Seconds from now on until when the transaction to be submitted to the stellar network sequence_number (int,optional): specify a specific sequence number ( will still be increased by one) instead of loading it from the account sign (bool,optional) : Do not sign and submit the transaction Raises: Exception: If asset not in correct format stellar_sdk.exceptions.BadRequestError: not enough funds for opertaion stellar_sdk.exceptions.BadRequestError: bad transfer authentication Returns: [type]: [description] """ issuer = None j.logger.info(f"Sending {amount} {asset} to {destination_address}") if asset != "XLM": assetStr = asset.split(":") if len(assetStr) != 2: raise Exception("Wrong asset format should be in format 'assetcode:issuer'") 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() if sequence_number: source_account.sequence = sequence_number transaction_builder = stellar_sdk.TransactionBuilder( source_account=source_account, network_passphrase=_NETWORK_PASSPHRASES[self.network.value], base_fee=base_fee, ) transaction_builder.append_payment_op( destination=destination_address, amount=str(amount), asset_code=asset, asset_issuer=issuer, source=source_account.account_id, ) transaction_builder.set_timeout(timeout) 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 == "TFT" or asset == "FreeTFT": if fund_transaction: transaction = self._fund_transaction(transaction=transaction) transaction = transaction["transaction_xdr"] if not sign: return transaction transaction = stellar_sdk.TransactionEnvelope.from_xdr(transaction, _NETWORK_PASSPHRASES[self.network.value]) my_keypair = stellar_sdk.Keypair.from_secret(self.secret) transaction.sign(my_keypair) response = horizon_server.submit_transaction(transaction) tx_hash = response["hash"] j.logger.info(f"Transaction hash: {tx_hash}") return tx_hash
def _create_vesting_account(self, owner_address) -> str: if self._is_multisig_account(owner_address): raise j.exceptions.Value("Multisig accounts are not supported") existing_escrow_address = self._check_has_vesting_account( owner_address) if existing_escrow_address: return existing_escrow_address escrow_kp = stellar_sdk.Keypair.random() escrow_address = escrow_kp.public_key horizon_server = self._get_horizon_server() base_fee = horizon_server.fetch_base_fee() # TODO: serialize with a gevent pool wallet = get_wallet() if not wallet: raise j.exceptions.Value("Service unavailable") activation_account = wallet.load_account() tftasset = wallet._get_asset() txb = (stellar_sdk.TransactionBuilder( activation_account, network_passphrase=self._get_network_passphrase( )).append_create_account_op( escrow_address, starting_balance="7.6").append_change_trust_op( "TFT", self._get_tft_issuer(), source=escrow_address)) activation_kp = stellar_sdk.Keypair.from_secret(wallet.secret) tx = txb.build() tx.sign(activation_kp) tx.sign(escrow_kp) horizon_server.submit_transaction(tx) # TODO: try except and clean up the unfinished vesting account vesting_account = horizon_server.load_account(escrow_address) txb = stellar_sdk.TransactionBuilder( vesting_account, network_passphrase=self._get_network_passphrase( )).append_ed25519_public_key_signer(owner_address, weight=5, source=escrow_address) recovery_transaction = self._create_recovery_transaction( escrow_address) txb.append_pre_auth_tx_signer(recovery_transaction.hash(), weight=10, source=escrow_address) for cosigner in self._get_cosigners(): txb.append_ed25519_public_key_signer(cosigner, weight=1, source=escrow_address) txb.append_manage_data_op(DATA_ENTRY_KEY, VESTING_SCHEME, source=escrow_address) txb.append_set_options_op(master_weight=0, low_threshold=10, med_threshold=10, high_threshold=10, source=escrow_address) tx = txb.build() tx.sign(escrow_kp) horizon_server.submit_transaction(tx) return escrow_address