diff --git a/lnbits/app.py b/lnbits/app.py index bc2c99d5..641deed0 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -55,7 +55,7 @@ def create_app(config_object="lnbits.settings") -> QuartTrio: def check_funding_source(app: QuartTrio) -> None: @app.before_serving async def check_wallet_status(): - error_message, balance = WALLET.status() + error_message, balance = await WALLET.status() if error_message: warnings.warn( f" × The backend for {WALLET.__class__.__name__} isn't working properly: '{error_message}'", diff --git a/lnbits/core/models.py b/lnbits/core/models.py index 8e429327..8ab58e67 100644 --- a/lnbits/core/models.py +++ b/lnbits/core/models.py @@ -141,9 +141,9 @@ class Payment(NamedTuple): return if self.is_out: - pending = WALLET.get_payment_status(self.checking_id) + pending = await WALLET.get_payment_status(self.checking_id) else: - pending = WALLET.get_invoice_status(self.checking_id) + pending = await WALLET.get_invoice_status(self.checking_id) await self.set_pending(pending.pending) diff --git a/lnbits/core/services.py b/lnbits/core/services.py index 2afb2921..91d53ca1 100644 --- a/lnbits/core/services.py +++ b/lnbits/core/services.py @@ -41,7 +41,7 @@ async def create_invoice( invoice_memo = None if description_hash else memo storeable_memo = memo - ok, checking_id, payment_request, error_message = WALLET.create_invoice( + ok, checking_id, payment_request, error_message = await WALLET.create_invoice( amount=amount, memo=invoice_memo, description_hash=description_hash ) if not ok: @@ -139,7 +139,7 @@ async def pay_invoice( await internal_invoice_paid.send(internal_checking_id) else: # actually pay the external invoice - payment: PaymentResponse = WALLET.pay_invoice(payment_request) + payment: PaymentResponse = await WALLET.pay_invoice(payment_request) if payment.ok and payment.checking_id: await create_payment( checking_id=payment.checking_id, @@ -255,4 +255,4 @@ async def check_invoice_status(wallet_id: str, payment_hash: str) -> PaymentStat if not payment: return PaymentStatus(None) - return WALLET.get_invoice_status(payment.checking_id) + return await WALLET.get_invoice_status(payment.checking_id) diff --git a/lnbits/extensions/diagonalley/crud.py b/lnbits/extensions/diagonalley/crud.py index a6543d1d..8e89c35a 100644 --- a/lnbits/extensions/diagonalley/crud.py +++ b/lnbits/extensions/diagonalley/crud.py @@ -274,7 +274,7 @@ def get_diagonalleys_orders(wallet_ids: Union[str, List[str]]) -> List[Orders]: f"SELECT * FROM orders WHERE wallet IN ({q})", (*wallet_ids,) ) for r in rows: - PAID = WALLET.get_invoice_status(r["invoiceid"]).paid + PAID = (await WALLET.get_invoice_status(r["invoiceid"])).paid if PAID: with open_ext_db("diagonalley") as db: db.execute( diff --git a/lnbits/extensions/lndhub/views_api.py b/lnbits/extensions/lndhub/views_api.py index 09caf54c..5f3b2a8b 100644 --- a/lnbits/extensions/lndhub/views_api.py +++ b/lnbits/extensions/lndhub/views_api.py @@ -133,7 +133,7 @@ async def lndhub_gettxs(): exclude_uncheckable=True, ): await payment.set_pending( - WALLET.get_payment_status(payment.checking_id).pending + (await WALLET.get_payment_status(payment.checking_id)).pending ) limit = int(request.args.get("limit", 200)) @@ -174,7 +174,7 @@ async def lndhub_getuserinvoices(): exclude_uncheckable=True, ): await invoice.set_pending( - WALLET.get_invoice_status(invoice.checking_id).pending + (await WALLET.get_invoice_status(invoice.checking_id)).pending ) limit = int(request.args.get("limit", 200)) diff --git a/lnbits/wallets/base.py b/lnbits/wallets/base.py index adc100cd..469f7ec0 100644 --- a/lnbits/wallets/base.py +++ b/lnbits/wallets/base.py @@ -1,5 +1,5 @@ from abc import ABC, abstractmethod -from typing import NamedTuple, Optional, AsyncGenerator +from typing import NamedTuple, Optional, AsyncGenerator, Coroutine class StatusResponse(NamedTuple): @@ -15,7 +15,9 @@ class InvoiceResponse(NamedTuple): class PaymentResponse(NamedTuple): - ok: bool + ok: Optional[ + bool + ] = None # when ok is None it means we don't know if this succeeded checking_id: Optional[str] = None # payment_hash, rcp_id fee_msat: int = 0 preimage: Optional[str] = None @@ -32,7 +34,7 @@ class PaymentStatus(NamedTuple): class Wallet(ABC): @abstractmethod - def status(self) -> StatusResponse: + def status(self) -> Coroutine[None, None, StatusResponse]: pass @abstractmethod @@ -41,19 +43,23 @@ class Wallet(ABC): amount: int, memo: Optional[str] = None, description_hash: Optional[bytes] = None, - ) -> InvoiceResponse: + ) -> Coroutine[None, None, InvoiceResponse]: pass @abstractmethod - def pay_invoice(self, bolt11: str) -> PaymentResponse: + def pay_invoice(self, bolt11: str) -> Coroutine[None, None, PaymentResponse]: pass @abstractmethod - def get_invoice_status(self, checking_id: str) -> PaymentStatus: + def get_invoice_status( + self, checking_id: str + ) -> Coroutine[None, None, PaymentStatus]: pass @abstractmethod - def get_payment_status(self, checking_id: str) -> PaymentStatus: + def get_payment_status( + self, checking_id: str + ) -> Coroutine[None, None, PaymentStatus]: pass @abstractmethod diff --git a/lnbits/wallets/clightning.py b/lnbits/wallets/clightning.py index f0028f06..b9f24f57 100644 --- a/lnbits/wallets/clightning.py +++ b/lnbits/wallets/clightning.py @@ -49,7 +49,7 @@ class CLightningWallet(Wallet): self.last_pay_index = inv["pay_index"] break - def status(self) -> StatusResponse: + async def status(self) -> StatusResponse: try: funds = self.ln.listfunds() return StatusResponse( @@ -60,7 +60,7 @@ class CLightningWallet(Wallet): error_message = f"lightningd '{exc.method}' failed with '{exc.error}'." return StatusResponse(error_message, 0) - def create_invoice( + async def create_invoice( self, amount: int, memo: Optional[str] = None, @@ -84,7 +84,7 @@ class CLightningWallet(Wallet): error_message = f"lightningd '{exc.method}' failed with '{exc.error}'." return InvoiceResponse(False, label, None, error_message) - def pay_invoice(self, bolt11: str) -> PaymentResponse: + async def pay_invoice(self, bolt11: str) -> PaymentResponse: try: r = self.ln.pay(bolt11) except RpcError as exc: @@ -94,7 +94,7 @@ class CLightningWallet(Wallet): preimage = r["payment_preimage"] return PaymentResponse(True, r["payment_hash"], fee_msat, preimage, None) - def get_invoice_status(self, checking_id: str) -> PaymentStatus: + async def get_invoice_status(self, checking_id: str) -> PaymentStatus: r = self.ln.listinvoices(checking_id) if not r["invoices"]: return PaymentStatus(False) @@ -102,7 +102,7 @@ class CLightningWallet(Wallet): return PaymentStatus(r["invoices"][0]["status"] == "paid") raise KeyError("supplied an invalid checking_id") - def get_payment_status(self, checking_id: str) -> PaymentStatus: + async def get_payment_status(self, checking_id: str) -> PaymentStatus: r = self.ln.call("listpays", {"payment_hash": checking_id}) if not r["pays"]: return PaymentStatus(False) diff --git a/lnbits/wallets/lnbits.py b/lnbits/wallets/lnbits.py index c8665872..786a4b03 100644 --- a/lnbits/wallets/lnbits.py +++ b/lnbits/wallets/lnbits.py @@ -25,8 +25,10 @@ class LNbitsWallet(Wallet): ) self.key = {"X-Api-Key": key} - def status(self) -> StatusResponse: - r = httpx.get(url=f"{self.endpoint}/api/v1/wallet", headers=self.key) + async def status(self) -> StatusResponse: + async with httpx.AsyncClient() as client: + r = await client.get(url=f"{self.endpoint}/api/v1/wallet", headers=self.key) + try: data = r.json() except: @@ -39,7 +41,7 @@ class LNbitsWallet(Wallet): return StatusResponse(None, data["balance"]) - def create_invoice( + async def create_invoice( self, amount: int, memo: Optional[str] = None, @@ -51,11 +53,12 @@ class LNbitsWallet(Wallet): else: data["memo"] = memo or "" - r = httpx.post( - url=f"{self.endpoint}/api/v1/payments", - headers=self.key, - json=data, - ) + async with httpx.AsyncClient() as client: + r = await client.post( + url=f"{self.endpoint}/api/v1/payments", + headers=self.key, + json=data, + ) ok, checking_id, payment_request, error_message = ( not r.is_error, None, @@ -71,12 +74,13 @@ class LNbitsWallet(Wallet): return InvoiceResponse(ok, checking_id, payment_request, error_message) - def pay_invoice(self, bolt11: str) -> PaymentResponse: - r = httpx.post( - url=f"{self.endpoint}/api/v1/payments", - headers=self.key, - json={"out": True, "bolt11": bolt11}, - ) + async def pay_invoice(self, bolt11: str) -> PaymentResponse: + async with httpx.AsyncClient() as client: + r = await client.post( + url=f"{self.endpoint}/api/v1/payments", + headers=self.key, + json={"out": True, "bolt11": bolt11}, + ) ok, checking_id, fee_msat, error_message = not r.is_error, None, 0, None if r.is_error: @@ -87,20 +91,22 @@ class LNbitsWallet(Wallet): return PaymentResponse(ok, checking_id, fee_msat, error_message) - def get_invoice_status(self, checking_id: str) -> PaymentStatus: - r = httpx.get( - url=f"{self.endpoint}/api/v1/payments/{checking_id}", headers=self.key - ) + async def get_invoice_status(self, checking_id: str) -> PaymentStatus: + async with httpx.AsyncClient() as client: + r = await client.get( + url=f"{self.endpoint}/api/v1/payments/{checking_id}", headers=self.key + ) if r.is_error: return PaymentStatus(None) return PaymentStatus(r.json()["paid"]) - def get_payment_status(self, checking_id: str) -> PaymentStatus: - r = httpx.get( - url=f"{self.endpoint}/api/v1/payments/{checking_id}", headers=self.key - ) + async def get_payment_status(self, checking_id: str) -> PaymentStatus: + async with httpx.AsyncClient() as client: + r = await client.get( + url=f"{self.endpoint}/api/v1/payments/{checking_id}", headers=self.key + ) if r.is_error: return PaymentStatus(None) diff --git a/lnbits/wallets/lndgrpc.py b/lnbits/wallets/lndgrpc.py index 36384008..25c9b283 100644 --- a/lnbits/wallets/lndgrpc.py +++ b/lnbits/wallets/lndgrpc.py @@ -112,7 +112,7 @@ class LndWallet(Wallet): macaroon_filepath=self.macaroon_path, ) - def status(self) -> StatusResponse: + async def status(self) -> StatusResponse: try: resp = self.rpc._ln_stub.ChannelBalance(ln.ChannelBalanceRequest()) except Exception as exc: @@ -120,7 +120,7 @@ class LndWallet(Wallet): return StatusResponse(None, resp.balance * 1000) - def create_invoice( + async def create_invoice( self, amount: int, memo: Optional[str] = None, @@ -144,7 +144,7 @@ class LndWallet(Wallet): payment_request = str(resp.payment_request) return InvoiceResponse(True, checking_id, payment_request, None) - def pay_invoice(self, bolt11: str) -> PaymentResponse: + async def pay_invoice(self, bolt11: str) -> PaymentResponse: resp = self.rpc.send_payment(payment_request=bolt11) if resp.payment_error: @@ -156,7 +156,7 @@ class LndWallet(Wallet): preimage = resp.payment_preimage.hex() return PaymentResponse(True, checking_id, fee_msat, preimage, None) - def get_invoice_status(self, checking_id: str) -> PaymentStatus: + async def get_invoice_status(self, checking_id: str) -> PaymentStatus: try: r_hash = parse_checking_id(checking_id) if len(r_hash) != 32: @@ -172,7 +172,7 @@ class LndWallet(Wallet): return PaymentStatus(None) - def get_payment_status(self, checking_id: str) -> PaymentStatus: + async def get_payment_status(self, checking_id: str) -> PaymentStatus: return PaymentStatus(True) async def paid_invoices_stream(self) -> AsyncGenerator[str, None]: diff --git a/lnbits/wallets/lndrest.py b/lnbits/wallets/lndrest.py index 1f8c3805..fa46c573 100644 --- a/lnbits/wallets/lndrest.py +++ b/lnbits/wallets/lndrest.py @@ -35,13 +35,13 @@ class LndRestWallet(Wallet): self.auth = {"Grpc-Metadata-macaroon": macaroon} self.cert = getenv("LND_REST_CERT") - def status(self) -> StatusResponse: + async def status(self) -> StatusResponse: try: - r = httpx.get( - f"{self.endpoint}/v1/balance/channels", - headers=self.auth, - verify=self.cert, - ) + async with httpx.AsyncClient(verify=self.cert) as client: + r = await client.get( + f"{self.endpoint}/v1/balance/channels", + headers=self.auth, + ) except (httpx.ConnectError, httpx.RequestError): return StatusResponse(f"Unable to connect to {self.endpoint}.", 0) @@ -54,7 +54,7 @@ class LndRestWallet(Wallet): return StatusResponse(None, int(data["balance"]) * 1000) - def create_invoice( + async def create_invoice( self, amount: int, memo: Optional[str] = None, @@ -71,12 +71,12 @@ class LndRestWallet(Wallet): else: data["memo"] = memo or "" - r = httpx.post( - url=f"{self.endpoint}/v1/invoices", - headers=self.auth, - verify=self.cert, - json=data, - ) + async with httpx.AsyncClient(verify=self.cert) as client: + r = await client.post( + url=f"{self.endpoint}/v1/invoices", + headers=self.auth, + json=data, + ) if r.is_error: error_message = r.text @@ -93,14 +93,14 @@ class LndRestWallet(Wallet): return InvoiceResponse(True, checking_id, payment_request, None) - def pay_invoice(self, bolt11: str) -> PaymentResponse: - r = httpx.post( - url=f"{self.endpoint}/v1/channels/transactions", - headers=self.auth, - verify=self.cert, - json={"payment_request": bolt11}, - timeout=180, - ) + async def pay_invoice(self, bolt11: str) -> PaymentResponse: + async with httpx.AsyncClient(verify=self.cert) as client: + r = await client.post( + url=f"{self.endpoint}/v1/channels/transactions", + headers=self.auth, + json={"payment_request": bolt11}, + timeout=180, + ) if r.is_error: error_message = r.text @@ -116,13 +116,14 @@ class LndRestWallet(Wallet): preimage = base64.b64decode(data["payment_preimage"]).hex() return PaymentResponse(True, checking_id, 0, preimage, None) - def get_invoice_status(self, checking_id: str) -> PaymentStatus: + async def get_invoice_status(self, checking_id: str) -> PaymentStatus: checking_id = checking_id.replace("_", "/") - r = httpx.get( - url=f"{self.endpoint}/v1/invoice/{checking_id}", - headers=self.auth, - verify=self.cert, - ) + + async with httpx.AsyncClient(verify=self.cert) as client: + r = await client.get( + url=f"{self.endpoint}/v1/invoice/{checking_id}", + headers=self.auth, + ) if r.is_error or not r.json().get("settled"): # this must also work when checking_id is not a hex recognizable by lnd @@ -131,13 +132,13 @@ class LndRestWallet(Wallet): return PaymentStatus(True) - def get_payment_status(self, checking_id: str) -> PaymentStatus: - r = httpx.get( - url=f"{self.endpoint}/v1/payments", - headers=self.auth, - verify=self.cert, - params={"max_payments": "20", "reversed": True}, - ) + async def get_payment_status(self, checking_id: str) -> PaymentStatus: + async with httpx.AsyncClient(verify=self.cert) as client: + r = await client.get( + url=f"{self.endpoint}/v1/payments", + headers=self.auth, + params={"max_payments": "20", "reversed": True}, + ) if r.is_error: return PaymentStatus(None) diff --git a/lnbits/wallets/lnpay.py b/lnbits/wallets/lnpay.py index 4309b46e..dc4a2e58 100644 --- a/lnbits/wallets/lnpay.py +++ b/lnbits/wallets/lnpay.py @@ -24,10 +24,11 @@ class LNPayWallet(Wallet): self.wallet_key = getenv("LNPAY_WALLET_KEY") or getenv("LNPAY_ADMIN_KEY") self.auth = {"X-Api-Key": getenv("LNPAY_API_KEY")} - def status(self) -> StatusResponse: + async def status(self) -> StatusResponse: url = f"{self.endpoint}/wallet/{self.wallet_key}" try: - r = httpx.get(url, headers=self.auth, timeout=60) + async with httpx.AsyncClient() as client: + r = await client.get(url, headers=self.auth, timeout=60) except (httpx.ConnectError, httpx.RequestError): return StatusResponse(f"Unable to connect to '{url}'", 0) @@ -43,7 +44,7 @@ class LNPayWallet(Wallet): return StatusResponse(None, data["balance"] * 1000) - def create_invoice( + async def create_invoice( self, amount: int, memo: Optional[str] = None, @@ -55,12 +56,13 @@ class LNPayWallet(Wallet): else: data["memo"] = memo or "" - r = httpx.post( - f"{self.endpoint}/wallet/{self.wallet_key}/invoice", - headers=self.auth, - json=data, - timeout=60, - ) + async with httpx.AsyncClient() as client: + r = await client.post( + f"{self.endpoint}/wallet/{self.wallet_key}/invoice", + headers=self.auth, + json=data, + timeout=60, + ) ok, checking_id, payment_request, error_message = ( r.status_code == 201, None, @@ -74,13 +76,14 @@ class LNPayWallet(Wallet): return InvoiceResponse(ok, checking_id, payment_request, error_message) - def pay_invoice(self, bolt11: str) -> PaymentResponse: - r = httpx.post( - f"{self.endpoint}/wallet/{self.wallet_key}/withdraw", - headers=self.auth, - json={"payment_request": bolt11}, - timeout=180, - ) + async def pay_invoice(self, bolt11: str) -> PaymentResponse: + async with httpx.AsyncClient() as client: + r = await client.post( + f"{self.endpoint}/wallet/{self.wallet_key}/withdraw", + headers=self.auth, + json={"payment_request": bolt11}, + timeout=180, + ) try: data = r.json() @@ -97,14 +100,15 @@ class LNPayWallet(Wallet): preimage = data["lnTx"]["payment_preimage"] return PaymentResponse(True, checking_id, fee_msat, preimage, None) - def get_invoice_status(self, checking_id: str) -> PaymentStatus: - return self.get_payment_status(checking_id) + async def get_invoice_status(self, checking_id: str) -> PaymentStatus: + return await self.get_payment_status(checking_id) - def get_payment_status(self, checking_id: str) -> PaymentStatus: - r = httpx.get( - url=f"{self.endpoint}/lntx/{checking_id}?fields=settled", - headers=self.auth, - ) + async def get_payment_status(self, checking_id: str) -> PaymentStatus: + async with httpx.AsyncClient() as client: + r = await client.get( + url=f"{self.endpoint}/lntx/{checking_id}?fields=settled", + headers=self.auth, + ) if r.is_error: return PaymentStatus(None) diff --git a/lnbits/wallets/lntxbot.py b/lnbits/wallets/lntxbot.py index d12766fc..a346cd43 100644 --- a/lnbits/wallets/lntxbot.py +++ b/lnbits/wallets/lntxbot.py @@ -27,12 +27,13 @@ class LntxbotWallet(Wallet): ) self.auth = {"Authorization": f"Basic {key}"} - def status(self) -> StatusResponse: - r = httpx.get( - f"{self.endpoint}/balance", - headers=self.auth, - timeout=40, - ) + async def status(self) -> StatusResponse: + async with httpx.AsyncClient() as client: + r = await client.get( + f"{self.endpoint}/balance", + headers=self.auth, + timeout=40, + ) try: data = r.json() except: @@ -45,7 +46,7 @@ class LntxbotWallet(Wallet): return StatusResponse(None, data["BTC"]["AvailableBalance"] * 1000) - def create_invoice( + async def create_invoice( self, amount: int, memo: Optional[str] = None, @@ -57,12 +58,13 @@ class LntxbotWallet(Wallet): else: data["memo"] = memo or "" - r = httpx.post( - f"{self.endpoint}/addinvoice", - headers=self.auth, - json=data, - timeout=40, - ) + async with httpx.AsyncClient() as client: + r = await client.post( + f"{self.endpoint}/addinvoice", + headers=self.auth, + json=data, + timeout=40, + ) if r.is_error: try: @@ -77,13 +79,14 @@ class LntxbotWallet(Wallet): data = r.json() return InvoiceResponse(True, data["payment_hash"], data["pay_req"], None) - def pay_invoice(self, bolt11: str) -> PaymentResponse: - r = httpx.post( - f"{self.endpoint}/payinvoice", - headers=self.auth, - json={"invoice": bolt11}, - timeout=40, - ) + async def pay_invoice(self, bolt11: str) -> PaymentResponse: + async with httpx.AsyncClient() as client: + r = await client.post( + f"{self.endpoint}/payinvoice", + headers=self.auth, + json={"invoice": bolt11}, + timeout=40, + ) if r.is_error: try: @@ -101,11 +104,12 @@ class LntxbotWallet(Wallet): preimage = data["payment_preimage"] return PaymentResponse(True, checking_id, fee_msat, preimage, None) - def get_invoice_status(self, checking_id: str) -> PaymentStatus: - r = httpx.post( - f"{self.endpoint}/invoicestatus/{checking_id}?wait=false", - headers=self.auth, - ) + async def get_invoice_status(self, checking_id: str) -> PaymentStatus: + async with httpx.AsyncClient() as client: + r = await client.post( + f"{self.endpoint}/invoicestatus/{checking_id}?wait=false", + headers=self.auth, + ) data = r.json() if r.is_error or "error" in data: @@ -116,11 +120,12 @@ class LntxbotWallet(Wallet): return PaymentStatus(True) - def get_payment_status(self, checking_id: str) -> PaymentStatus: - r = httpx.post( - url=f"{self.endpoint}/paymentstatus/{checking_id}", - headers=self.auth, - ) + async def get_payment_status(self, checking_id: str) -> PaymentStatus: + async with httpx.AsyncClient() as client: + r = await client.post( + url=f"{self.endpoint}/paymentstatus/{checking_id}", + headers=self.auth, + ) data = r.json() if r.is_error or "error" in data: diff --git a/lnbits/wallets/opennode.py b/lnbits/wallets/opennode.py index 56db598a..8354e819 100644 --- a/lnbits/wallets/opennode.py +++ b/lnbits/wallets/opennode.py @@ -1,4 +1,3 @@ -import json import trio # type: ignore import hmac import httpx @@ -31,13 +30,14 @@ class OpenNodeWallet(Wallet): ) self.auth = {"Authorization": key} - def status(self) -> StatusResponse: + async def status(self) -> StatusResponse: try: - r = httpx.get( - f"{self.endpoint}/v1/account/balance", - headers=self.auth, - timeout=40, - ) + async with httpx.AsyncClient() as client: + r = await client.get( + f"{self.endpoint}/v1/account/balance", + headers=self.auth, + timeout=40, + ) except (httpx.ConnectError, httpx.RequestError): return StatusResponse(f"Unable to connect to '{self.endpoint}'", 0) @@ -47,7 +47,7 @@ class OpenNodeWallet(Wallet): return StatusResponse(None, data["balance"]["BTC"] / 100_000_000_000) - def create_invoice( + async def create_invoice( self, amount: int, memo: Optional[str] = None, @@ -56,16 +56,17 @@ class OpenNodeWallet(Wallet): if description_hash: raise Unsupported("description_hash") - r = httpx.post( - f"{self.endpoint}/v1/charges", - headers=self.auth, - json={ - "amount": amount, - "description": memo or "", - "callback_url": url_for("webhook_listener", _external=True), - }, - timeout=40, - ) + async with httpx.AsyncClient() as client: + r = await client.post( + f"{self.endpoint}/v1/charges", + headers=self.auth, + json={ + "amount": amount, + "description": memo or "", + "callback_url": url_for("webhook_listener", _external=True), + }, + timeout=40, + ) if r.is_error: error_message = r.json()["message"] @@ -76,13 +77,14 @@ class OpenNodeWallet(Wallet): payment_request = data["lightning_invoice"]["payreq"] return InvoiceResponse(True, checking_id, payment_request, None) - def pay_invoice(self, bolt11: str) -> PaymentResponse: - r = httpx.post( - f"{self.endpoint}/v2/withdrawals", - headers=self.auth, - json={"type": "ln", "address": bolt11}, - timeout=180, - ) + async def pay_invoice(self, bolt11: str) -> PaymentResponse: + async with httpx.AsyncClient() as client: + r = await client.post( + f"{self.endpoint}/v2/withdrawals", + headers=self.auth, + json={"type": "ln", "address": bolt11}, + timeout=180, + ) if r.is_error: error_message = r.json()["message"] @@ -93,16 +95,22 @@ class OpenNodeWallet(Wallet): fee_msat = data["fee"] * 1000 return PaymentResponse(True, checking_id, fee_msat, None, None) - def get_invoice_status(self, checking_id: str) -> PaymentStatus: - r = httpx.get(f"{self.endpoint}/v1/charge/{checking_id}", headers=self.auth) + async def get_invoice_status(self, checking_id: str) -> PaymentStatus: + async with httpx.AsyncClient() as client: + r = await client.get( + f"{self.endpoint}/v1/charge/{checking_id}", headers=self.auth + ) if r.is_error: return PaymentStatus(None) statuses = {"processing": None, "paid": True, "unpaid": False} return PaymentStatus(statuses[r.json()["data"]["status"]]) - def get_payment_status(self, checking_id: str) -> PaymentStatus: - r = httpx.get(f"{self.endpoint}/v1/withdrawal/{checking_id}", headers=self.auth) + async def get_payment_status(self, checking_id: str) -> PaymentStatus: + async with httpx.AsyncClient() as client: + r = await client.get( + f"{self.endpoint}/v1/withdrawal/{checking_id}", headers=self.auth + ) if r.is_error: return PaymentStatus(None) diff --git a/lnbits/wallets/spark.py b/lnbits/wallets/spark.py index 715cc0c9..1586bbb0 100644 --- a/lnbits/wallets/spark.py +++ b/lnbits/wallets/spark.py @@ -28,7 +28,7 @@ class SparkWallet(Wallet): self.token = getenv("SPARK_TOKEN") def __getattr__(self, key): - def call(*args, **kwargs): + async def call(*args, **kwargs): if args and kwargs: raise TypeError( f"must supply either named arguments or a list of arguments, not both: {args} {kwargs}" @@ -40,12 +40,14 @@ class SparkWallet(Wallet): else: params = {} - r = httpx.post( - self.url + "/rpc", - headers={"X-Access": self.token}, - json={"method": key, "params": params}, - timeout=40, - ) + async with httpx.AsyncClient() as client: + r = await client.post( + self.url + "/rpc", + headers={"X-Access": self.token}, + json={"method": key, "params": params}, + timeout=40, + ) + try: data = r.json() except: @@ -61,9 +63,9 @@ class SparkWallet(Wallet): return call - def status(self) -> StatusResponse: + async def status(self) -> StatusResponse: try: - funds = self.listfunds() + funds = await self.listfunds() except (httpx.ConnectError, httpx.RequestError): return StatusResponse("Couldn't connect to Spark server", 0) except (SparkError, UnknownError) as e: @@ -74,7 +76,7 @@ class SparkWallet(Wallet): sum([ch["channel_sat"] * 1000 for ch in funds["channels"]]), ) - def create_invoice( + async def create_invoice( self, amount: int, memo: Optional[str] = None, @@ -85,13 +87,13 @@ class SparkWallet(Wallet): try: if description_hash: - r = self.invoicewithdescriptionhash( + r = await self.invoicewithdescriptionhash( msatoshi=amount * 1000, label=label, description_hash=description_hash.hex(), ) else: - r = self.invoice( + r = await self.invoice( msatoshi=amount * 1000, label=label, description=memo or "", @@ -103,9 +105,9 @@ class SparkWallet(Wallet): return InvoiceResponse(ok, checking_id, payment_request, error_message) - def pay_invoice(self, bolt11: str) -> PaymentResponse: + async def pay_invoice(self, bolt11: str) -> PaymentResponse: try: - r = self.pay(bolt11) + r = await self.pay(bolt11) except (SparkError, UnknownError) as exc: return PaymentResponse(False, None, 0, None, str(exc)) @@ -113,15 +115,15 @@ class SparkWallet(Wallet): preimage = r["payment_preimage"] return PaymentResponse(True, r["payment_hash"], fee_msat, preimage, None) - def get_invoice_status(self, checking_id: str) -> PaymentStatus: - r = self.listinvoices(label=checking_id) + async def get_invoice_status(self, checking_id: str) -> PaymentStatus: + r = await self.listinvoices(label=checking_id) if not r or not r.get("invoices"): return PaymentStatus(None) if r["invoices"][0]["status"] == "unpaid": return PaymentStatus(False) return PaymentStatus(True) - def get_payment_status(self, checking_id: str) -> PaymentStatus: + async def get_payment_status(self, checking_id: str) -> PaymentStatus: # check if it's 32 bytes hex if len(checking_id) != 64: return PaymentStatus(None) @@ -131,7 +133,7 @@ class SparkWallet(Wallet): return PaymentStatus(None) # ask sparko - r = self.listpays(payment_hash=checking_id) + r = await self.listpays(payment_hash=checking_id) if not r["pays"]: return PaymentStatus(False) if r["pays"][0]["payment_hash"] == checking_id: diff --git a/lnbits/wallets/void.py b/lnbits/wallets/void.py index a3c2a982..591fa042 100644 --- a/lnbits/wallets/void.py +++ b/lnbits/wallets/void.py @@ -11,7 +11,7 @@ from .base import ( class VoidWallet(Wallet): - def create_invoice( + async def create_invoice( self, amount: int, memo: Optional[str] = None, @@ -19,19 +19,19 @@ class VoidWallet(Wallet): ) -> InvoiceResponse: raise Unsupported("") - def status(self) -> StatusResponse: + async def status(self) -> StatusResponse: return StatusResponse( "This backend does nothing, it is here just as a placeholder, you must configure an actual backend before being able to do anything useful with LNbits.", 0, ) - def pay_invoice(self, bolt11: str) -> PaymentResponse: + async def pay_invoice(self, bolt11: str) -> PaymentResponse: raise Unsupported("") - def get_invoice_status(self, checking_id: str) -> PaymentStatus: + async def get_invoice_status(self, checking_id: str) -> PaymentStatus: raise Unsupported("") - def get_payment_status(self, checking_id: str) -> PaymentStatus: + async def get_payment_status(self, checking_id: str) -> PaymentStatus: raise Unsupported("") async def paid_invoices_stream(self) -> AsyncGenerator[str, None]: