Compare commits

..

No commits in common. "9a9539224c6bf109445f75dece45be56a585c7ab" and "f0f224fd3fbad8e410d365ba3582940e4fb01a78" have entirely different histories.

2 changed files with 17 additions and 18 deletions

View file

@ -106,16 +106,16 @@ def stubs(monkeypatch):
async def get_wallet(wallet_id): async def get_wallet(wallet_id):
return state["wallet"] return state["wallet"]
def cache_get(key, default=None): async def satoshis_amount_as_fiat(amount, currency):
# A warm LNbits rate cache: 1 BTC = 80,000 USD → 123456 sats = 98.7648 assert currency == "USD"
return 80_000.0 if key == "btc-price-USD" else default return amount * 0.0008 # 123456 sats → 98.7648
monkeypatch.setattr(v, "get_card_by_external_id", get_card_by_external_id) monkeypatch.setattr(v, "get_card_by_external_id", get_card_by_external_id)
monkeypatch.setattr(v, "update_card_counter", update_card_counter) monkeypatch.setattr(v, "update_card_counter", update_card_counter)
monkeypatch.setattr(v, "get_hits_today", get_hits_today) monkeypatch.setattr(v, "get_hits_today", get_hits_today)
monkeypatch.setattr(v, "create_hit", create_hit) monkeypatch.setattr(v, "create_hit", create_hit)
monkeypatch.setattr(v, "get_wallet", get_wallet) monkeypatch.setattr(v, "get_wallet", get_wallet)
monkeypatch.setattr(v.cache, "get", cache_get) monkeypatch.setattr(v, "satoshis_amount_as_fiat", satoshis_amount_as_fiat)
monkeypatch.setattr( monkeypatch.setattr(
v.settings, "lnbits_default_accounting_currency", state["default_currency"] v.settings, "lnbits_default_accounting_currency", state["default_currency"]
) )
@ -209,18 +209,18 @@ async def test_session_falls_back_to_instance_default_currency(stubs, monkeypatc
p, c = make_sun(counter=6) p, c = make_sun(counter=6)
out = await v.api_session(p, c, FakeRequest(), "ext123") out = await v.api_session(p, c, FakeRequest(), "ext123")
assert out["currency"] == "USD" assert out["currency"] == "USD"
assert out["fiat"] == pytest.approx(5 / 100_000_000 * 80_000) assert out["fiat"] == pytest.approx(5 * 0.0008)
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_session_leaves_fiat_null_on_a_cold_rate_cache(stubs, monkeypatch): async def test_session_survives_rate_failure(stubs, monkeypatch):
# No external rate lookup ever happens on the unlock path: a cache miss async def boom(amount, currency):
# simply means the terminal prices the balance itself. raise ValueError("no rate")
monkeypatch.setattr(v.cache, "get", lambda key, default=None: default)
monkeypatch.setattr(v, "satoshis_amount_as_fiat", boom)
p, c = make_sun(counter=6) p, c = make_sun(counter=6)
out = await v.api_session(p, c, FakeRequest(), "ext123") out = await v.api_session(p, c, FakeRequest(), "ext123")
assert out["authenticated"] is True assert out["authenticated"] is True
assert out["currency"] == "USD"
assert out["fiat"] is None assert out["fiat"] is None

View file

@ -8,7 +8,7 @@ from fastapi import APIRouter, HTTPException, Query, Request
from lnbits.core.crud import get_wallet from lnbits.core.crud import get_wallet
from lnbits.core.services import create_invoice, pay_invoice from lnbits.core.services import create_invoice, pay_invoice
from lnbits.settings import settings from lnbits.settings import settings
from lnbits.utils.cache import cache from lnbits.utils.exchange_rates import satoshis_amount_as_fiat
from lnurl import ( from lnurl import (
CallbackUrl, CallbackUrl,
LightningInvoice, LightningInvoice,
@ -507,10 +507,8 @@ async def api_session(p, c, request: Request, external_id: str):
# Balance + fiat, the way the LNbits wallet page shows them: the wallet's # Balance + fiat, the way the LNbits wallet page shows them: the wallet's
# own currency first (per-wallet setting, LNbits ≥ 1.6), then the # own currency first (per-wallet setting, LNbits ≥ 1.6), then the
# instance's default accounting currency. The fiat figure comes ONLY from # instance's default accounting currency; no currency → no fiat, and a
# LNbits' already-warm rate cache: this response gates the terminal # rate failure never fails the session.
# unlocking, and a cold lookup queries external exchanges (~1 s). Cache
# miss → `fiat: null`; the terminal prices the sats itself.
wallet = await get_wallet(card.wallet) wallet = await get_wallet(card.wallet)
balance_msat = int(wallet.balance_msat) if wallet else 0 balance_msat = int(wallet.balance_msat) if wallet else 0
currency = (getattr(wallet, "currency", None) if wallet else None) or getattr( currency = (getattr(wallet, "currency", None) if wallet else None) or getattr(
@ -518,9 +516,10 @@ async def api_session(p, c, request: Request, external_id: str):
) )
fiat = None fiat = None
if currency: if currency:
price = cache.get(f"btc-price-{currency}") # fiat per BTC, as btc_price() try:
if isinstance(price, (int, float)) and price > 0: fiat = await satoshis_amount_as_fiat(balance_msat / 1000, currency)
fiat = balance_msat / 1000 / 100_000_000 * float(price) except Exception:
fiat = None
return { return {
"authenticated": True, "authenticated": True,