mirror of
https://github.com/Routstr/routstr-core.git
synced 2026-10-05 20:28:23 +00:00
fix wallet instance
This commit is contained in:
+2
-2
@@ -5,7 +5,7 @@ from fastapi import APIRouter, Request
|
|||||||
from fastapi.responses import HTMLResponse
|
from fastapi.responses import HTMLResponse
|
||||||
from sqlmodel import select
|
from sqlmodel import select
|
||||||
|
|
||||||
from .cashu import WALLET
|
from .cashu import wallet
|
||||||
from .db import ApiKey, create_session
|
from .db import ApiKey, create_session
|
||||||
|
|
||||||
admin_router = APIRouter(prefix="/admin")
|
admin_router = APIRouter(prefix="/admin")
|
||||||
@@ -112,7 +112,7 @@ async def dashboard(request: Request) -> str:
|
|||||||
# avoid rounding issues.
|
# avoid rounding issues.
|
||||||
total_user_balance = sum(key.balance for key in api_keys) // 1000
|
total_user_balance = sum(key.balance for key in api_keys) // 1000
|
||||||
# Fetch balance from cashu
|
# 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
|
owner_balance = current_balance - total_user_balance
|
||||||
|
|
||||||
return f"""<!DOCTYPE html>
|
return f"""<!DOCTYPE html>
|
||||||
|
|||||||
+10
-13
@@ -16,19 +16,19 @@ DEV_LN_ADDRESS = "routstr@minibits.cash"
|
|||||||
DEVS_DONATION_RATE = float(os.environ.get("DEVS_DONATION_RATE", 0.021)) # 2.1%
|
DEVS_DONATION_RATE = float(os.environ.get("DEVS_DONATION_RATE", 0.021)) # 2.1%
|
||||||
NSEC = os.environ["NSEC"] # Nostr private key for the wallet
|
NSEC = os.environ["NSEC"] # Nostr private key for the wallet
|
||||||
|
|
||||||
WALLET: Wallet | None = None
|
wallet_instance: Wallet | None = None
|
||||||
|
|
||||||
|
|
||||||
async def init_wallet() -> None:
|
async def init_wallet() -> None:
|
||||||
global WALLET
|
global wallet_instance
|
||||||
WALLET = await Wallet.create(nsec=NSEC)
|
wallet_instance = await Wallet.create(nsec=NSEC)
|
||||||
|
|
||||||
|
|
||||||
def wallet() -> Wallet:
|
def wallet() -> Wallet:
|
||||||
global WALLET
|
global wallet_instance
|
||||||
if WALLET is None:
|
if wallet_instance is None:
|
||||||
raise ValueError("Wallet not initialized")
|
raise ValueError("Wallet not initialized")
|
||||||
return WALLET
|
return wallet_instance
|
||||||
|
|
||||||
|
|
||||||
async def delete_key_if_zero_balance(key: ApiKey, session: AsyncSession) -> None:
|
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:
|
if key.refund_address is None:
|
||||||
raise ValueError("Refund address not set.")
|
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:
|
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.delete(key)
|
||||||
await session.commit()
|
await session.commit()
|
||||||
return refund_token
|
return refund_token
|
||||||
|
|
||||||
|
|
||||||
async def redeem(cashu_token: str, lnurl: str) -> int:
|
async def redeem(cashu_token: str, lnurl: str) -> int:
|
||||||
assert WALLET is not None, "Wallet not initialized"
|
amount_sats, _ = await wallet().redeem(cashu_token)
|
||||||
amount_sats, _ = await WALLET.redeem(cashu_token)
|
await wallet().send_to_lnurl(lnurl, amount=amount_sats)
|
||||||
await WALLET.send_to_lnurl(lnurl, amount=amount_sats)
|
|
||||||
return amount_sats
|
return amount_sats
|
||||||
|
|||||||
@@ -99,48 +99,6 @@ async def test_refund_balance_with_address(
|
|||||||
mock_refund.assert_called_once()
|
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
|
@pytest.mark.asyncio
|
||||||
async def test_topup_balance_endpoint(
|
async def test_topup_balance_endpoint(
|
||||||
|
|||||||
@@ -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()
|
|
||||||
Reference in New Issue
Block a user