def list_transactions(wallet_file, test_mode=True, trezor_mode=False): network_settings = get_network_settings(test_mode=test_mode) if not trezor_mode: (private_key, public_key) = load_wallet(wallet_file=wallet_file) k = Keypair.from_secret(secret=private_key) else: public_key = get_trezor_public_key() k = Keypair.from_public_key(public_key=public_key) server = Server(network_settings.get("horizon_url")) response = server.transactions().for_account( account_id=k.public_key).call() print(json.dumps(response, indent=4))
from stellar_sdk import Server server = Server(horizon_url="https://horizon-testnet.stellar.org") account_id = "GASOCNHNNLYFNMDJYQ3XFMI7BYHIOCFW3GJEOWRPEGK2TDPGTG2E5EDW" last_cursor = 'now' # or load where you left off def tx_handler(tx_response): print(tx_response) for tx in server.transactions().for_account(account_id).cursor( last_cursor).stream(): tx_handler(tx)
class HorizonProvider(StellarProvider): """ Horizon is a software that allows you to query nodes via http. This should be the only thing we need since horizon also indexes the data it holds. """ def __init__(self, horizon_url='https://horizon-testnet.stellar.org/'): self.server = Server(horizon_url=horizon_url) def _make_request(self, fun, *args, **kwargs): """ Used to make the request to the horizon server. It will catch the library specific exceptions and raise the exceptions used in this project. :param fun: api call to make :param args: args for the api call :param kwargs: args for the api call :return: response form the api call """ try: return fun(*args, **kwargs) except stellar_sdk.exceptions.ConnectionError: raise ConnectionException('Could not connect to the server') except stellar_sdk.exceptions.NotFoundError as e: # This exception is used by the check_account_created function if fun == self.server.load_account: raise e raise RequestException('Check the request params, the resource could not be found') except stellar_sdk.exceptions.BadRequestError as e: raise RequestException('Your request had an error') except stellar_sdk.exceptions.BadResponseError: raise RequestException('The server response had an error') def submit_transaction(self, tx): return self._make_request(self.server.submit_transaction, tx)['hash'] def get_balance(self, address): # We only care about the native token right now. return self._make_request(self.server.accounts().account_id(address).call)['balances'][0]['balance'] def get_transactions(self, address): response = self._make_request( self.server.transactions().for_account(address).limit(200).include_failed(True).call) transactions = response['_embedded']['records'] return self._normalize_transactions(transactions) def _normalize_transactions(self, transactions): """ Transform a list of Transactions from the api into the format used in this project :param transactions: List of transactions from the api :return: A list of Transaction objects """ transactions_to_return = [] for tx in transactions: transactions_to_return.append(self._normalize_transaction(tx)) return transactions_to_return def _normalize_transaction(self, tx) -> Transaction: """ Transform a transaction from the api into a transaction object this project uses. :param tx: transaction from api :return: Transaction object """ max_time_bound_string = tx.get('valid_before') # the date we get from the api ends with z to indicate utc we have to remove this to parse it max_time_bound = None if max_time_bound_string is None else datetime.fromisoformat(max_time_bound_string[:-1]) min_time_bound_string = tx.get('valid_after') min_time_bound = None if min_time_bound_string is None else datetime.fromisoformat(min_time_bound_string[:-1]) return Transaction( hash=tx['hash'], date_time=datetime.fromisoformat(tx['created_at'][:-1]), fee=int(tx['fee_charged']), operation_count=tx['operation_count'], source_account=tx['source_account'], succeeded=tx['successful'], sequence_number=tx['source_account_sequence'], transaction_envelope=tx['envelope_xdr'], ledger_nr=tx['ledger'], min_time_bound=min_time_bound, max_time_bound=max_time_bound ) def get_base_fee(self): return self._make_request(self.server.fetch_base_fee) def get_ledger_height(self): response = self._make_request(self.server.ledgers().limit(1).order().call) return response['_embedded']['records'][0]['sequence'] def get_account_sequence(self, address): return int(self._make_request(self.server.accounts().account_id(address).call)["sequence"]) def check_account_created(self, address) -> bool: try: self._make_request(self.server.load_account, address) except NotFoundError: return False return True
class StellarWallet: """ Stellar Hot Wallet Handler on chain for live net use self.server = Server(horizon_url="https://horizon.stellar.org") # Live network """ def __init__(self, horizon_url: str, integrated_coins): helpers = Helpers() secret_details = helpers.read_json_file(file_name="walletSecrets.json") # Load Stellar wallet secrets public_details = helpers.read_json_file(file_name="hotWallets.json") # Load hot wallet details self.integrated_coins = integrated_coins self.public_key = public_details["xlm"] self.dev_key = public_details["xlmDev"] self.private_key = secret_details['stellar'] self.root_keypair = Keypair.from_secret(self.private_key) self.root_account = Account(account_id=self.root_keypair.public_key, sequence=1) self.server = Server(horizon_url=horizon_url) # Testnet # Decide network type if horizon_url == "https://horizon-testnet.stellar.org": self.network_phrase = Network.TESTNET_NETWORK_PASSPHRASE self.network_type = 'testnet' else: self.network_phrase = Network.PUBLIC_NETWORK_PASSPHRASE self.network_type = 'pub-net' def create_stellar_account(self): """ Creates inactive stellar account which needs to be activated by depositing lumens """ try: key_pair = Keypair.random() public_key = key_pair.public_key private_key = key_pair.secret return {f'address': f'{public_key}', f'secret': f'{private_key}', "network": f'{self.network_type}'} except NotFoundError: return {} def generate_uri(self, address: str, memo: str): """ Returns Transaction as envelope """ return stellar_uri.PayStellarUri(destination=address, memo=TextMemo(text=memo), asset=Asset.native(), network_passphrase=self.network_phrase, message='Deposit to Discord', ).to_uri() @staticmethod def __filter_error(result_code): if 'op_no_trust' in result_code: return 'no trust' elif 'op_no_source_account' in result_code: return 'No source account provided' else: return result_code def check_if_account_activate(self, address): "Try to load account on the network" try: self.server.load_account(account_id=address) return True except NotFoundError: return False def get_stellar_hot_wallet_details(self): """ Return the stellar hot wallet balance :return: """ data = self.server.accounts().account_id(account_id=self.public_key).call() if 'status' not in data: data.pop('_links') data.pop('data') data.pop('flags') data.pop('last_modified_ledger') data.pop('sequence') data.pop('subentry_count') data.pop('thresholds') data.pop('signers') data.pop('id') data.pop('paging_token') return data else: return {} def decode_transaction_envelope(self, envelope_xdr): """ Decode envelope and get details Credits to overcat : https://stellar.stackexchange.com/questions/3022/how-can-i-get-the-value-of-the-stellar-transaction/3025#3025 :param envelope_xdr: Xdr envelope from stellar network :return: Decoded transaction details """ te = TransactionEnvelope.from_xdr(envelope_xdr, self.network_phrase) operations = te.transaction.operations # TODO make multiple payments inside one transaction for op in operations: if isinstance(op, Payment): asset = op.asset.to_dict() if asset.get('type') == 'native': asset['code'] = 'XLM' # Appending XLM code to asset incase if native asset["amount"] = op.to_xdr_amount(op.amount) # TODO count all deposits return asset def get_incoming_transactions(self, pag=None): """ Gets all incoming transactions and removes certain values :return: List of incoming transfers """ data = self.server.transactions().for_account(account_id=self.public_key).include_failed(False).order( desc=False).cursor(cursor=pag).limit(200).call() to_process = list() for tx in data['_embedded']['records']: # Get transaction envelope if tx['source_account'] != self.public_key and tx['successful'] is True: # Get only incoming transactions tx.pop('_links') tx.pop('fee_charged') tx.pop('id') tx.pop('fee_account') tx.pop('fee_meta_xdr') tx.pop('ledger') tx.pop('max_fee') tx.pop('operation_count') tx.pop('result_meta_xdr') tx.pop('result_xdr') tx.pop('signatures') tx['asset_type'] = self.decode_transaction_envelope(envelope_xdr=tx['envelope_xdr']) tx.pop('envelope_xdr') to_process.append(tx) return to_process @staticmethod def check_if_memo(memo): """ Check if memo has been provided :param memo: :return: """ if memo != 'none': return True else: return False def token_withdrawal(self, address, token, amount: str): """ Amount as full """ if token != 'xlm': asset_issuer = self.integrated_coins[token.lower()]["assetIssuer"] else: asset_issuer = None source_account = self.server.load_account(self.public_key) tx = TransactionBuilder( source_account=source_account, network_passphrase=self.network_phrase, base_fee=self.server.fetch_base_fee()).append_payment_op( asset_issuer=asset_issuer, destination=address, asset_code=token.upper(), amount=amount).set_timeout(30).build() tx.sign(self.root_keypair) try: resp = self.server.submit_transaction(tx) details = self.decode_transaction_envelope(envelope_xdr=resp['envelope_xdr']) end_details = { "asset": details['code'], "explorer": resp['_links']['transaction']['href'], "hash": resp['hash'], "ledger": resp['ledger'], "destination": address, "amount": details['amount'] } return end_details except exceptions.BadRequestError as e: # get operation from result_codes to be processed error = self.__filter_error(result_code=e.extras["result_codes"]['operations']) return { "error": f'{error} with {token.upper()} issuer' } def establish_trust(self, private_key, token): """ Amount as full """ # Load user secret and get account user_key_pair = Keypair.from_secret(private_key) root_account = Account(account_id=user_key_pair.public_key, sequence=1) public_key = root_account.account_id asset_issuer = self.integrated_coins[token.lower()]["assetIssuer"] try: source_account = self.server.load_account(public_key) tx = TransactionBuilder( source_account=source_account, network_passphrase=self.network_phrase, base_fee=self.server.fetch_base_fee()).append_change_trust_op(asset_code=f'{token.upper()}', asset_issuer=asset_issuer).set_timeout( 30).build() tx.sign(private_key) self.server.submit_transaction(tx) return True except exceptions.NotFoundError: return False
from stellar_sdk import Server server = Server(horizon_url="https://horizon-testnet.stellar.org") # get a list of transactions that occurred in ledger 1400 transactions = server.transactions().for_ledger(1400).call() print(transactions) # get a list of transactions submitted by a particular account transactions = server.transactions() \ .for_account(account_id="GASOCNHNNLYFNMDJYQ3XFMI7BYHIOCFW3GJEOWRPEGK2TDPGTG2E5EDW") \ .call() print(transactions)