fix wallet instance

This commit is contained in:
shroominic
2025-06-30 17:54:43 +00:00
parent e5354fd773
commit ccf213aae1
4 changed files with 12 additions and 115 deletions
+2 -2
View File
@@ -5,7 +5,7 @@ from fastapi import APIRouter, Request
from fastapi.responses import HTMLResponse
from sqlmodel import select
from .cashu import WALLET
from .cashu import wallet
from .db import ApiKey, create_session
admin_router = APIRouter(prefix="/admin")
@@ -112,7 +112,7 @@ async def dashboard(request: Request) -> str:
# avoid rounding issues.
total_user_balance = sum(key.balance for key in api_keys) // 1000
# Fetch balance from cashu
current_balance = (await WALLET.fetch_wallet_state()).balance
current_balance = (await wallet().fetch_wallet_state()).balance
owner_balance = current_balance - total_user_balance
return f"""<!DOCTYPE html>
+10 -13
View File
@@ -16,19 +16,19 @@ DEV_LN_ADDRESS = "routstr@minibits.cash"
DEVS_DONATION_RATE = float(os.environ.get("DEVS_DONATION_RATE", 0.021)) # 2.1%
NSEC = os.environ["NSEC"] # Nostr private key for the wallet
WALLET: Wallet | None = None
wallet_instance: Wallet | None = None
async def init_wallet() -> None:
global WALLET
WALLET = await Wallet.create(nsec=NSEC)
global wallet_instance
wallet_instance = await Wallet.create(nsec=NSEC)
def wallet() -> Wallet:
global WALLET
if WALLET is None:
global wallet_instance
if wallet_instance is None:
raise ValueError("Wallet not initialized")
return WALLET
return wallet_instance
async def delete_key_if_zero_balance(key: ApiKey, session: AsyncSession) -> None:
@@ -187,20 +187,17 @@ async def refund_balance(amount_msats: int, key: ApiKey, session: AsyncSession)
if key.refund_address is None:
raise ValueError("Refund address not set.")
assert WALLET is not None, "Wallet not initialized"
return await WALLET.send_to_lnurl(key.refund_address, amount=amount_sats)
return await wallet().send_to_lnurl(key.refund_address, amount=amount_sats)
async def x_cashu_refund(key: ApiKey, session: AsyncSession) -> str:
assert WALLET is not None, "Wallet not initialized"
refund_token = await WALLET.send(key.balance)
refund_token = await wallet().send(key.balance)
await session.delete(key)
await session.commit()
return refund_token
async def redeem(cashu_token: str, lnurl: str) -> int:
assert WALLET is not None, "Wallet not initialized"
amount_sats, _ = await WALLET.redeem(cashu_token)
await WALLET.send_to_lnurl(lnurl, amount=amount_sats)
amount_sats, _ = await wallet().redeem(cashu_token)
await wallet().send_to_lnurl(lnurl, amount=amount_sats)
return amount_sats
-42
View File
@@ -99,48 +99,6 @@ async def test_refund_balance_with_address(
mock_refund.assert_called_once()
@pytest.mark.asyncio
async def test_refund_balance_without_address(
async_client: AsyncClient, test_session: AsyncSession
) -> None:
"""Test refunding balance when no refund address is set."""
# Create key without refund address - with unique ID
unique_id = str(uuid.uuid4())[:8]
api_key = f"test-key-no-refund-{unique_id}"
key = ApiKey(
hashed_key=api_key,
balance=500000,
refund_address=None,
total_spent=0,
total_requests=0,
)
test_session.add(key)
await test_session.commit()
# Mock the WALLET instance at the router.account module level
with patch("router.account.WALLET") as mock_wallet:
mock_wallet.send = AsyncMock(return_value="cashuBqQSEQ...")
response = await async_client.post(
"/v1/wallet/refund", headers={"Authorization": f"Bearer sk-{api_key}"}
)
assert response.status_code == 200
data = response.json()
assert data["recipient"] is None
assert data["msats"] == 500000
assert data["token"] == "cashuBqQSEQ..."
# Verify wallet.send was called with the correct amount (msats converted to sats)
mock_wallet.send.assert_called_once_with(500)
# Verify the API key was deleted after refund
deleted = await test_session.get(ApiKey, api_key)
assert deleted is None
@pytest.mark.asyncio
async def test_topup_balance_endpoint(
-58
View File
@@ -1,58 +0,0 @@
import asyncio
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from router.main import app, lifespan
from tests.conftest import TEST_ENV
@pytest.mark.asyncio
async def test_background_tasks_cancel_on_shutdown() -> None:
pricing_started = asyncio.Event()
pricing_cancelled = asyncio.Event()
async def fake_update() -> None:
pricing_started.set()
try:
await asyncio.Event().wait()
except asyncio.CancelledError:
pricing_cancelled.set()
raise
refund_started = asyncio.Event()
refund_cancelled = asyncio.Event()
async def fake_refund() -> None:
refund_started.set()
try:
await asyncio.Event().wait()
except asyncio.CancelledError:
refund_cancelled.set()
raise
with patch.dict("os.environ", TEST_ENV, clear=True):
mock_wallet = AsyncMock()
mock_wallet.__aenter__ = AsyncMock(return_value=mock_wallet)
mock_wallet.__aexit__ = AsyncMock(return_value=None)
mock_state = MagicMock()
mock_state.balance = 1000
mock_wallet.fetch_wallet_state = AsyncMock(return_value=mock_state)
mock_wallet.send_to_lnurl = AsyncMock(return_value=100)
mock_wallet.redeem = AsyncMock(return_value=(1, "sat"))
mock_wallet.send = AsyncMock(return_value="cashuAtoken123")
with (
patch("router.cashu.Wallet.create", AsyncMock(return_value=mock_wallet)),
patch("router.cashu.WALLET", mock_wallet),
):
with (
patch("router.main.update_sats_pricing", new=fake_update),
patch("router.main.check_for_refunds", new=fake_refund),
):
async with lifespan(app):
await pricing_started.wait()
await refund_started.wait()
assert pricing_cancelled.is_set()
assert refund_cancelled.is_set()