def handle(self, *args, **options): server = Server(horizon_url="https://horizon-testnet.stellar.org") ledgers = server.ledgers().order(desc=True).call() Ledger.objects.all().delete() Ledger(raw_json=json.dumps(ledgers)).save()
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