remove wait_for_sync from all calls

This commit is contained in:
2025-05-16 16:03:28 +02:00
parent d53aab8c2e
commit 4201f7ce79
2 changed files with 103 additions and 26 deletions

View File

@@ -114,6 +114,12 @@ class LnurlWithdrawBody(BaseModel):
amount_msat: int amount_msat: int
comment: Optional[str] = None comment: Optional[str] = None
# Exchange Rate Models
class ExchangeRateResponse(BaseModel):
currency: Optional[str] = None
rate: Optional[float] = None
rates: Optional[Dict[str, float]] = None
# --- Dependencies --- # --- Dependencies ---
async def get_api_key(api_key: str = Header(None, alias=API_KEY_NAME)): async def get_api_key(api_key: str = Header(None, alias=API_KEY_NAME)):
if not API_KEY: if not API_KEY:
@@ -334,6 +340,61 @@ async def withdraw(
except Exception as e: except Exception as e:
raise HTTPException(status_code=500, detail=str(e)) raise HTTPException(status_code=500, detail=str(e))
@app.get("/exchange_rates/{currency}", response_model=ExchangeRateResponse)
async def get_exchange_rate(
currency: Optional[str] = None,
api_key: str = Depends(get_api_key),
handler: PaymentHandler = Depends(get_payment_handler)
):
"""
Get current exchange rates, optionally filtered by currency.
Args:
currency: Optional currency code (e.g., 'EUR', 'USD'). If not provided, returns all rates.
Returns:
Exchange rate information for the specified currency or all available currencies.
"""
logger.info(f"Received exchange rate request for currency: {currency}")
try:
result = handler.get_exchange_rate(currency)
# Format response based on whether a specific currency was requested
if currency:
return ExchangeRateResponse(
currency=result['currency'],
rate=result['rate']
)
else:
return ExchangeRateResponse(rates=result)
except ValueError as e:
logger.error(f"Currency not found: {str(e)}")
raise HTTPException(status_code=404, detail=str(e))
except Exception as e:
logger.error(f"Error fetching exchange rate: {str(e)}")
logger.exception("Full error details:")
raise HTTPException(status_code=500, detail=str(e))
@app.get("/exchange_rates", response_model=ExchangeRateResponse)
async def get_all_exchange_rates(
api_key: str = Depends(get_api_key),
handler: PaymentHandler = Depends(get_payment_handler)
):
"""
Get all available exchange rates.
Returns:
Dictionary of all available exchange rates.
"""
logger.info("Received request for all exchange rates")
try:
result = handler.get_exchange_rate()
return ExchangeRateResponse(rates=result)
except Exception as e:
logger.error(f"Error fetching exchange rates: {str(e)}")
logger.exception("Full error details:")
raise HTTPException(status_code=500, detail=str(e))
app.include_router(ln_router) app.include_router(ln_router)
if __name__ == "__main__": if __name__ == "__main__":

View File

@@ -274,7 +274,6 @@ class PaymentHandler:
""" """
logger.debug("Entering get_info") logger.debug("Entering get_info")
try: try:
self.wait_for_sync()
info = self.instance.get_info() info = self.instance.get_info()
# Convert info object to dictionary for easier handling # Convert info object to dictionary for easier handling
info_dict = { info_dict = {
@@ -305,7 +304,6 @@ class PaymentHandler:
""" """
logger.debug(f"Entering list_payments with params: {params}") logger.debug(f"Entering list_payments with params: {params}")
try: try:
#self.wait_for_sync()
from_ts = int(params.get('from_timestamp')) if params and params.get('from_timestamp') is not None else None from_ts = int(params.get('from_timestamp')) if params and params.get('from_timestamp') is not None else None
to_ts = int(params.get('to_timestamp')) if params and params.get('to_timestamp') is not None else None to_ts = int(params.get('to_timestamp')) if params and params.get('to_timestamp') is not None else None
offset = int(params.get('offset')) if params and params.get('offset') is not None else None offset = int(params.get('offset')) if params and params.get('offset') is not None else None
@@ -380,7 +378,6 @@ class PaymentHandler:
""" """
logger.debug(f"Entering get_payment with identifier: {identifier}, type: {identifier_type}") logger.debug(f"Entering get_payment with identifier: {identifier}, type: {identifier_type}")
try: try:
self.wait_for_sync()
req = None req = None
if identifier_type == 'payment_hash': if identifier_type == 'payment_hash':
req = GetPaymentRequest.PAYMENT_HASH(identifier) req = GetPaymentRequest.PAYMENT_HASH(identifier)
@@ -439,7 +436,6 @@ class PaymentHandler:
""" """
logger.debug(f"Entering send_payment to {destination} (amount_sat={amount_sat}, amount_asset={amount_asset}, asset_id={asset_id}, drain={drain})") logger.debug(f"Entering send_payment to {destination} (amount_sat={amount_sat}, amount_asset={amount_asset}, asset_id={asset_id}, drain={drain})")
try: try:
self.wait_for_sync()
amount_obj = None amount_obj = None
if drain: if drain:
@@ -560,7 +556,6 @@ class PaymentHandler:
""" """
logger.debug("Entering fetch_buy_bitcoin_limits") logger.debug("Entering fetch_buy_bitcoin_limits")
try: try:
self.wait_for_sync()
limits = self.instance.fetch_onchain_limits() # Onchain limits apply to Buy/Sell limits = self.instance.fetch_onchain_limits() # Onchain limits apply to Buy/Sell
limits_dict = { limits_dict = {
'receive': limits.receive.__dict__ if limits.receive else None, 'receive': limits.receive.__dict__ if limits.receive else None,
@@ -589,7 +584,6 @@ class PaymentHandler:
""" """
logger.debug(f"Entering prepare_buy_bitcoin (provider={provider}, amount={amount_sat})") logger.debug(f"Entering prepare_buy_bitcoin (provider={provider}, amount={amount_sat})")
try: try:
self.wait_for_sync()
buy_provider = getattr(BuyBitcoinProvider, provider.upper(), None) buy_provider = getattr(BuyBitcoinProvider, provider.upper(), None)
if not buy_provider: if not buy_provider:
logger.warning(f"Invalid buy bitcoin provider: {provider}") logger.warning(f"Invalid buy bitcoin provider: {provider}")
@@ -623,7 +617,6 @@ class PaymentHandler:
""" """
logger.debug("Entering buy_bitcoin") logger.debug("Entering buy_bitcoin")
try: try:
self.wait_for_sync()
# Check if it's the correct type of SDK object # Check if it's the correct type of SDK object
if not isinstance(prepare_response, PrepareBuyBitcoinResponse): if not isinstance(prepare_response, PrepareBuyBitcoinResponse):
logger.error(f"buy_bitcoin expects PrepareBuyBitcoinResponse object, but received {type(prepare_response)}.") logger.error(f"buy_bitcoin expects PrepareBuyBitcoinResponse object, but received {type(prepare_response)}.")
@@ -649,7 +642,6 @@ class PaymentHandler:
""" """
logger.debug("Entering list_fiat_currencies") logger.debug("Entering list_fiat_currencies")
try: try:
self.wait_for_sync() # Fiat data might need sync
currencies = self.instance.list_fiat_currencies() currencies = self.instance.list_fiat_currencies()
currencies_list = [c.__dict__ for c in currencies] currencies_list = [c.__dict__ for c in currencies]
logger.debug(f"Listed {len(currencies_list)} fiat currencies.") logger.debug(f"Listed {len(currencies_list)} fiat currencies.")
@@ -669,7 +661,6 @@ class PaymentHandler:
""" """
logger.debug("Entering fetch_fiat_rates") logger.debug("Entering fetch_fiat_rates")
try: try:
self.wait_for_sync() # Fiat data might need sync
rates = self.instance.fetch_fiat_rates() rates = self.instance.fetch_fiat_rates()
rates_list = [r.__dict__ for r in rates] rates_list = [r.__dict__ for r in rates]
logger.debug(f"Fetched {len(rates_list)} fiat rates.") logger.debug(f"Fetched {len(rates_list)} fiat rates.")
@@ -694,7 +685,6 @@ class PaymentHandler:
""" """
logger.debug(f"Entering parse_input with input: {input_str}") logger.debug(f"Entering parse_input with input: {input_str}")
try: try:
self.wait_for_sync() # Parsing might require network interaction
parsed_input = self.instance.parse(input_str) parsed_input = self.instance.parse(input_str)
# Convert the specific InputType object to a dictionary # Convert the specific InputType object to a dictionary
# Access .data on the *instance* of the parsed input, not the type # Access .data on the *instance* of the parsed input, not the type
@@ -749,7 +739,6 @@ class PaymentHandler:
""" """
logger.debug(f"Entering prepare_lnurl_pay (amount={amount_sat}, comment={comment})") logger.debug(f"Entering prepare_lnurl_pay (amount={amount_sat}, comment={comment})")
try: try:
self.wait_for_sync()
# Check if it's the correct type of SDK object # Check if it's the correct type of SDK object
if not isinstance(data, LnUrlPayRequestData): if not isinstance(data, LnUrlPayRequestData):
logger.error(f"prepare_lnurl_pay expects LnUrlPayRequestData object, but received {type(data)}.") logger.error(f"prepare_lnurl_pay expects LnUrlPayRequestData object, but received {type(data)}.")
@@ -793,7 +782,6 @@ class PaymentHandler:
""" """
logger.debug("Entering lnurl_pay") logger.debug("Entering lnurl_pay")
try: try:
self.wait_for_sync()
# Check if it's the correct type of SDK object # Check if it's the correct type of SDK object
if not isinstance(prepare_response, PrepareLnUrlPayResponse): if not isinstance(prepare_response, PrepareLnUrlPayResponse):
logger.error(f"lnurl_pay expects PrepareLnUrlPayResponse object, but received {type(prepare_response)}.") logger.error(f"lnurl_pay expects PrepareLnUrlPayResponse object, but received {type(prepare_response)}.")
@@ -826,7 +814,6 @@ class PaymentHandler:
""" """
logger.debug("Entering lnurl_auth") logger.debug("Entering lnurl_auth")
try: try:
self.wait_for_sync()
# Check if it's the correct type of SDK object # Check if it's the correct type of SDK object
if not isinstance(data, LnUrlAuthRequestData): if not isinstance(data, LnUrlAuthRequestData):
logger.error(f"lnurl_auth expects LnUrlAuthRequestData object, but received {type(data)}.") logger.error(f"lnurl_auth expects LnUrlAuthRequestData object, but received {type(data)}.")
@@ -865,7 +852,6 @@ class PaymentHandler:
""" """
logger.debug(f"Entering lnurl_withdraw (amount_msat={amount_msat}, comment={comment})") logger.debug(f"Entering lnurl_withdraw (amount_msat={amount_msat}, comment={comment})")
try: try:
self.wait_for_sync()
# Check if it's the correct type of SDK object # Check if it's the correct type of SDK object
if not isinstance(data, LnUrlWithdrawRequestData): if not isinstance(data, LnUrlWithdrawRequestData):
logger.error(f"lnurl_withdraw expects LnUrlWithdrawRequestData object, but received {type(data)}.") logger.error(f"lnurl_withdraw expects LnUrlWithdrawRequestData object, but received {type(data)}.")
@@ -953,7 +939,6 @@ class PaymentHandler:
""" """
logger.debug(f"Entering pay_onchain to {address}") logger.debug(f"Entering pay_onchain to {address}")
try: try:
self.wait_for_sync()
# Check if it's the correct type of SDK object # Check if it's the correct type of SDK object
if not isinstance(prepare_response, PreparePayOnchainResponse): if not isinstance(prepare_response, PreparePayOnchainResponse):
logger.error(f"pay_onchain expects PreparePayOnchainResponse object, but received {type(prepare_response)}.") logger.error(f"pay_onchain expects PreparePayOnchainResponse object, but received {type(prepare_response)}.")
@@ -990,7 +975,6 @@ class PaymentHandler:
""" """
logger.debug("Entering list_refundable_payments") logger.debug("Entering list_refundable_payments")
try: try:
self.wait_for_sync() # Ensure sync before executing
refundable_payments = self.instance.list_refundables() refundable_payments = self.instance.list_refundables()
logger.debug(f"Found {len(refundable_payments)} refundable payments.") logger.debug(f"Found {len(refundable_payments)} refundable payments.")
logger.debug("Exiting list_refundable_payments") logger.debug("Exiting list_refundable_payments")
@@ -1018,7 +1002,6 @@ class PaymentHandler:
# Using getattr with a default for logging in case refundable_swap is None or malformed (though type hint should prevent this) # Using getattr with a default for logging in case refundable_swap is None or malformed (though type hint should prevent this)
logger.debug(f"Entering execute_refund for swap {getattr(refundable_swap, 'swap_address', 'N/A')} to {refund_address} with fee rate {fee_rate_sat_per_vbyte}") logger.debug(f"Entering execute_refund for swap {getattr(refundable_swap, 'swap_address', 'N/A')} to {refund_address} with fee rate {fee_rate_sat_per_vbyte}")
try: try:
self.wait_for_sync()
# Check if it's the correct type of SDK object # Check if it's the correct type of SDK object
if not isinstance(refundable_swap, RefundableSwap): if not isinstance(refundable_swap, RefundableSwap):
logger.error(f"execute_refund expects RefundableSwap object, but received {type(refundable_swap)}.") logger.error(f"execute_refund expects RefundableSwap object, but received {type(refundable_swap)}.")
@@ -1062,7 +1045,6 @@ class PaymentHandler:
""" """
logger.debug("Entering rescan_swaps") logger.debug("Entering rescan_swaps")
try: try:
self.wait_for_sync() # Ensure sync before executing
self.instance.rescan_onchain_swaps() self.instance.rescan_onchain_swaps()
logger.info("Onchain swaps rescan initiated.") logger.info("Onchain swaps rescan initiated.")
logger.debug("Exiting rescan_swaps") logger.debug("Exiting rescan_swaps")
@@ -1083,7 +1065,6 @@ class PaymentHandler:
""" """
logger.debug("Entering recommended_fees") logger.debug("Entering recommended_fees")
try: try:
self.wait_for_sync() # Fee estimates might need network
fees = self.instance.recommended_fees() fees = self.instance.recommended_fees()
# Assuming recommended_fees returns an object with __dict__ or similar fee structure # Assuming recommended_fees returns an object with __dict__ or similar fee structure
fees_dict = fees.__dict__ if fees else {} # Convert to dict fees_dict = fees.__dict__ if fees else {} # Convert to dict
@@ -1105,7 +1086,6 @@ class PaymentHandler:
""" """
logger.debug("Entering handle_payments_waiting_fee_acceptance") logger.debug("Entering handle_payments_waiting_fee_acceptance")
try: try:
self.wait_for_sync()
logger.info("Checking for payments waiting for fee acceptance...") logger.info("Checking for payments waiting for fee acceptance...")
# Filter for WAITING_FEE_ACCEPTANCE state # Filter for WAITING_FEE_ACCEPTANCE state
payments_waiting = self.instance.list_payments( payments_waiting = self.instance.list_payments(
@@ -1207,7 +1187,6 @@ class PaymentHandler:
""" """
logger.debug(f"Entering register_webhook with URL: {webhook_url}") logger.debug(f"Entering register_webhook with URL: {webhook_url}")
try: try:
self.wait_for_sync() # Might require network
# Basic URL format validation (can be made more robust) # Basic URL format validation (can be made more robust)
if not isinstance(webhook_url, str) or not webhook_url.startswith('https://'): if not isinstance(webhook_url, str) or not webhook_url.startswith('https://'):
logger.warning(f"Invalid webhook_url provided: {webhook_url}") logger.warning(f"Invalid webhook_url provided: {webhook_url}")
@@ -1230,7 +1209,6 @@ class PaymentHandler:
""" """
logger.debug("Entering unregister_webhook") logger.debug("Entering unregister_webhook")
try: try:
self.wait_for_sync() # Might require network
self.instance.unregister_webhook() self.instance.unregister_webhook()
logger.info("Webhook unregistered.") logger.info("Webhook unregistered.")
logger.debug("Exiting unregister_webhook") logger.debug("Exiting unregister_webhook")
@@ -1257,7 +1235,6 @@ class PaymentHandler:
# Log truncated message to avoid logging potentially sensitive full messages # Log truncated message to avoid logging potentially sensitive full messages
logger.debug(f"Entering sign_message with message (truncated): {message[:50]}...") logger.debug(f"Entering sign_message with message (truncated): {message[:50]}...")
try: try:
self.wait_for_sync()
if not isinstance(message, str) or not message: if not isinstance(message, str) or not message:
logger.warning("Invalid or empty message provided for signing.") logger.warning("Invalid or empty message provided for signing.")
raise ValueError("Message to sign must be a non-empty string.") raise ValueError("Message to sign must be a non-empty string.")
@@ -1304,7 +1281,6 @@ class PaymentHandler:
""" """
logger.debug(f"Entering check_message for message (truncated): {message[:50]}...") logger.debug(f"Entering check_message for message (truncated): {message[:50]}...")
try: try:
self.wait_for_sync() # Might require network to verify
if not isinstance(message, str) or not message: if not isinstance(message, str) or not message:
logger.warning("Invalid or empty message provided for checking.") logger.warning("Invalid or empty message provided for checking.")
raise ValueError("Message to check must be a non-empty string.") raise ValueError("Message to check must be a non-empty string.")
@@ -1343,7 +1319,6 @@ class PaymentHandler:
""" """
logger.debug("Entering fetch_lightning_limits") logger.debug("Entering fetch_lightning_limits")
try: try:
self.wait_for_sync()
limits = self.instance.fetch_lightning_limits() limits = self.instance.fetch_lightning_limits()
limits_dict = { limits_dict = {
'receive': limits.receive.__dict__ if limits.receive else None, 'receive': limits.receive.__dict__ if limits.receive else None,
@@ -1368,7 +1343,6 @@ class PaymentHandler:
""" """
logger.debug("Entering fetch_onchain_limits") logger.debug("Entering fetch_onchain_limits")
try: try:
self.wait_for_sync()
limits = self.instance.fetch_onchain_limits() limits = self.instance.fetch_onchain_limits()
limits_dict = { limits_dict = {
'receive': limits.receive.__dict__ if limits.receive else None, 'receive': limits.receive.__dict__ if limits.receive else None,
@@ -1484,4 +1458,46 @@ class PaymentHandler:
except Exception as e: except Exception as e:
logger.error(f"Error checking payment status for {destination[:30]}...: {str(e)}") logger.error(f"Error checking payment status for {destination[:30]}...: {str(e)}")
logger.exception("Full error details:") logger.exception("Full error details:")
raise
def get_exchange_rate(self, currency: str = None) -> Dict[str, Any]:
"""
Fetches current exchange rates, optionally filtered by currency.
Args:
currency: Optional currency code (e.g., 'EUR', 'USD'). If provided, returns only that rate.
Returns:
Dictionary containing exchange rates. Format:
If currency specified: {'currency': 'EUR', 'rate': 123.45}
If no currency: {'EUR': 123.45, 'USD': 234.56, ...}
Raises:
ValueError: If specified currency is not found
Exception: For any SDK errors
"""
logger.debug(f"Entering get_exchange_rate for currency: {currency}")
try:
rates = self.instance.fetch_fiat_rates()
rates_dict = {}
# Convert rates to dictionary
for rate in rates:
rates_dict[rate.coin] = rate.value
if currency:
currency = currency.upper()
if currency not in rates_dict:
logger.warning(f"Requested currency {currency} not found in available rates")
raise ValueError(f"Exchange rate not available for currency: {currency}")
logger.info(f"Found exchange rate for {currency}: {rates_dict[currency]}")
return {
'currency': currency,
'rate': rates_dict[currency]
}
logger.info(f"Returning all exchange rates for {len(rates_dict)} currencies")
return rates_dict
except Exception as e:
logger.error(f"Error fetching exchange rate: {str(e)}")
logger.exception("Full error details:")
raise raise