mirror of
https://github.com/aljazceru/nutshell.git
synced 2025-12-22 19:34:18 +01:00
[Wallet] Define responses for API (#233)
* Define responses for wallet API * Make format
This commit is contained in:
75
cashu/wallet/api/responses.py
Normal file
75
cashu/wallet/api/responses.py
Normal file
@@ -0,0 +1,75 @@
|
||||
from typing import Dict, List, Union
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from ...core.base import Invoice, P2SHScript
|
||||
|
||||
|
||||
class PayResponse(BaseModel):
|
||||
amount: int
|
||||
fee: int
|
||||
amount_with_fee: int
|
||||
initial_balance: int
|
||||
balance: int
|
||||
|
||||
|
||||
class InvoiceResponse(BaseModel):
|
||||
amount: int
|
||||
invoice: Union[Invoice, None] = None
|
||||
hash: Union[str, None] = None
|
||||
initial_balance: int
|
||||
balance: int
|
||||
|
||||
|
||||
class BalanceResponse(BaseModel):
|
||||
balance: int
|
||||
keysets: Union[Dict, None] = None
|
||||
mints: Union[Dict, None] = None
|
||||
|
||||
|
||||
class SendResponse(BaseModel):
|
||||
balance: int
|
||||
token: str
|
||||
npub: Union[str, None] = None
|
||||
|
||||
|
||||
class ReceiveResponse(BaseModel):
|
||||
initial_balance: int
|
||||
balance: int
|
||||
|
||||
|
||||
class BurnResponse(BaseModel):
|
||||
balance: int
|
||||
|
||||
|
||||
class PendingResponse(BaseModel):
|
||||
pending_token: Dict
|
||||
|
||||
|
||||
class LockResponse(BaseModel):
|
||||
P2SH: Union[str, None]
|
||||
|
||||
|
||||
class LocksResponse(BaseModel):
|
||||
locks: List[P2SHScript]
|
||||
|
||||
|
||||
class InvoicesResponse(BaseModel):
|
||||
invoices: List[Invoice]
|
||||
|
||||
|
||||
class WalletsResponse(BaseModel):
|
||||
wallets: Dict
|
||||
|
||||
|
||||
class InfoResponse(BaseModel):
|
||||
version: str
|
||||
wallet: str
|
||||
debug: bool
|
||||
cashu_dir: str
|
||||
mint_url: str
|
||||
settings: Union[str, None]
|
||||
tor: bool
|
||||
nostr_public_key: Union[str, None] = None
|
||||
nostr_relays: List[str] = []
|
||||
socks_proxy: Union[str, None] = None
|
||||
@@ -18,6 +18,20 @@ from ...wallet.helpers import deserialize_token_from_string, init_wallet, receiv
|
||||
from ...wallet.nostr import receive_nostr, send_nostr
|
||||
from ...wallet.wallet import Wallet as Wallet
|
||||
from .api_helpers import verify_mints
|
||||
from .responses import (
|
||||
BalanceResponse,
|
||||
BurnResponse,
|
||||
InfoResponse,
|
||||
InvoiceResponse,
|
||||
InvoicesResponse,
|
||||
LockResponse,
|
||||
LocksResponse,
|
||||
PayResponse,
|
||||
PendingResponse,
|
||||
ReceiveResponse,
|
||||
SendResponse,
|
||||
WalletsResponse,
|
||||
)
|
||||
|
||||
router: APIRouter = APIRouter()
|
||||
|
||||
@@ -48,7 +62,7 @@ async def start_wallet():
|
||||
await init_wallet(wallet)
|
||||
|
||||
|
||||
@router.post("/pay", name="Pay lightning invoice")
|
||||
@router.post("/pay", name="Pay lightning invoice", response_model=PayResponse)
|
||||
async def pay(
|
||||
invoice: str = Query(default=..., description="Lightning invoice to pay"),
|
||||
mint: str = Query(
|
||||
@@ -77,16 +91,18 @@ async def pay(
|
||||
_, send_proofs = await wallet.split_to_send(wallet.proofs, total_amount)
|
||||
await wallet.pay_lightning(send_proofs, invoice)
|
||||
await wallet.load_proofs()
|
||||
return {
|
||||
"amount": total_amount - fee_reserve_sat,
|
||||
"fee": fee_reserve_sat,
|
||||
"amount_with_fee": total_amount,
|
||||
"initial_balance": initial_balance,
|
||||
"balance": wallet.available_balance,
|
||||
}
|
||||
return PayResponse(
|
||||
amount=total_amount - fee_reserve_sat,
|
||||
fee=fee_reserve_sat,
|
||||
amount_with_fee=total_amount,
|
||||
initial_balance=initial_balance,
|
||||
balance=wallet.available_balance,
|
||||
)
|
||||
|
||||
|
||||
@router.post("/invoice", name="Request lightning invoice")
|
||||
@router.post(
|
||||
"/invoice", name="Request lightning invoice", response_model=InvoiceResponse
|
||||
)
|
||||
async def invoice(
|
||||
amount: int = Query(default=..., description="Amount to request in invoice"),
|
||||
hash: str = Query(default=None, description="Hash of paid invoice"),
|
||||
@@ -100,44 +116,45 @@ async def invoice(
|
||||
initial_balance = wallet.available_balance
|
||||
if not settings.lightning:
|
||||
r = await wallet.mint(amount)
|
||||
return {
|
||||
"amount": amount,
|
||||
"balance": wallet.available_balance,
|
||||
"initial_balance": initial_balance,
|
||||
}
|
||||
return InvoiceResponse(
|
||||
amount=amount,
|
||||
balance=wallet.available_balance,
|
||||
initial_balance=initial_balance,
|
||||
)
|
||||
elif amount and not hash:
|
||||
invoice = await wallet.request_mint(amount)
|
||||
return {
|
||||
"invoice": invoice,
|
||||
"balance": wallet.available_balance,
|
||||
"initial_balance": initial_balance,
|
||||
}
|
||||
return InvoiceResponse(
|
||||
invoice=invoice,
|
||||
balance=wallet.available_balance,
|
||||
initial_balance=initial_balance,
|
||||
)
|
||||
elif amount and hash:
|
||||
await wallet.mint(amount, hash)
|
||||
return {
|
||||
"amount": amount,
|
||||
"hash": hash,
|
||||
"balance": wallet.available_balance,
|
||||
"initial_balance": initial_balance,
|
||||
}
|
||||
return InvoiceResponse(
|
||||
amount=amount,
|
||||
hash=hash,
|
||||
balance=wallet.available_balance,
|
||||
initial_balance=initial_balance,
|
||||
)
|
||||
return
|
||||
|
||||
|
||||
@router.get("/balance", name="Balance", summary="Display balance.")
|
||||
@router.get(
|
||||
"/balance",
|
||||
name="Balance",
|
||||
summary="Display balance.",
|
||||
response_model=BalanceResponse,
|
||||
)
|
||||
async def balance():
|
||||
await wallet.load_proofs()
|
||||
result: dict = {"balance": wallet.available_balance}
|
||||
keyset_balances = wallet.balance_per_keyset()
|
||||
if len(keyset_balances) > 0:
|
||||
result.update({"keysets": keyset_balances})
|
||||
mint_balances = await wallet.balance_per_minturl()
|
||||
if len(mint_balances) > 0:
|
||||
result.update({"mints": mint_balances})
|
||||
|
||||
return result
|
||||
return BalanceResponse(
|
||||
balance=wallet.available_balance, keysets=keyset_balances, mints=mint_balances
|
||||
)
|
||||
|
||||
|
||||
@router.post("/send", name="Send tokens")
|
||||
@router.post("/send", name="Send tokens", response_model=SendResponse)
|
||||
async def send_command(
|
||||
amount: int = Query(default=..., description="Amount to send"),
|
||||
nostr: str = Query(default=None, description="Send to nostr pubkey"),
|
||||
@@ -156,27 +173,23 @@ async def send_command(
|
||||
balance, token = await send(wallet, amount, lock, legacy=False)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
|
||||
return {"balance": balance, "token": token}
|
||||
return SendResponse(balance=balance, token=token)
|
||||
else:
|
||||
try:
|
||||
token, pubkey = await send_nostr(wallet, amount, nostr)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
|
||||
return {
|
||||
"balance": wallet.available_balance,
|
||||
"token": token,
|
||||
"npub": pubkey,
|
||||
}
|
||||
return SendResponse(balance=wallet.available_balance, token=token, npub=pubkey)
|
||||
|
||||
|
||||
@router.post("/receive", name="Receive tokens")
|
||||
@router.post("/receive", name="Receive tokens", response_model=ReceiveResponse)
|
||||
async def receive_command(
|
||||
token: str = Query(default=None, description="Token to receive"),
|
||||
lock: str = Query(default=None, description="Unlock tokens"),
|
||||
nostr: bool = Query(default=False, description="Receive tokens via nostr"),
|
||||
all: bool = Query(default=False, description="Receive all pending tokens"),
|
||||
):
|
||||
result = {"initial_balance": wallet.available_balance}
|
||||
initial_balance = wallet.available_balance
|
||||
if token:
|
||||
try:
|
||||
tokenObj: TokenV3 = await deserialize_token_from_string(token)
|
||||
@@ -223,11 +236,10 @@ async def receive_command(
|
||||
detail="enter token or use either flag --nostr or --all.",
|
||||
)
|
||||
assert balance
|
||||
result.update({"balance": balance})
|
||||
return result
|
||||
return ReceiveResponse(initial_balance=initial_balance, balance=balance)
|
||||
|
||||
|
||||
@router.post("/burn", name="Burn spent tokens")
|
||||
@router.post("/burn", name="Burn spent tokens", response_model=BurnResponse)
|
||||
async def burn(
|
||||
token: str = Query(default=None, description="Token to burn"),
|
||||
all: bool = Query(default=False, description="Burn all spent tokens"),
|
||||
@@ -268,10 +280,10 @@ async def burn(
|
||||
await wallet.invalidate(proofs, check_spendable=False)
|
||||
else:
|
||||
await wallet.invalidate(proofs)
|
||||
return {"balance": wallet.available_balance}
|
||||
return BurnResponse(balance=wallet.available_balance)
|
||||
|
||||
|
||||
@router.get("/pending", name="Show pending tokens")
|
||||
@router.get("/pending", name="Show pending tokens", response_model=PendingResponse)
|
||||
async def pending(
|
||||
number: int = Query(default=None, description="Show only n pending tokens"),
|
||||
offset: int = Query(
|
||||
@@ -312,35 +324,33 @@ async def pending(
|
||||
}
|
||||
}
|
||||
)
|
||||
return result
|
||||
return PendingResponse(pending_token=result)
|
||||
|
||||
|
||||
@router.get("/lock", name="Generate receiving lock")
|
||||
@router.get("/lock", name="Generate receiving lock", response_model=LockResponse)
|
||||
async def lock():
|
||||
p2shscript = await wallet.create_p2sh_lock()
|
||||
txin_p2sh_address = p2shscript.address
|
||||
return {"P2SH": txin_p2sh_address}
|
||||
return LockResponse(P2SH=txin_p2sh_address)
|
||||
|
||||
|
||||
@router.get("/locks", name="Show unused receiving locks")
|
||||
@router.get("/locks", name="Show unused receiving locks", response_model=LocksResponse)
|
||||
async def locks():
|
||||
locks = await get_unused_locks(db=wallet.db)
|
||||
if len(locks):
|
||||
return {"locks": locks}
|
||||
else:
|
||||
return {"locks": []}
|
||||
return LocksResponse(locks=locks)
|
||||
|
||||
|
||||
@router.get("/invoices", name="List all pending invoices")
|
||||
@router.get(
|
||||
"/invoices", name="List all pending invoices", response_model=InvoicesResponse
|
||||
)
|
||||
async def invoices():
|
||||
invoices = await get_lightning_invoices(db=wallet.db)
|
||||
if len(invoices):
|
||||
return {"invoices": invoices}
|
||||
else:
|
||||
return {"invoices": []}
|
||||
return InvoicesResponse(invoices=invoices)
|
||||
|
||||
|
||||
@router.get("/wallets", name="List all available wallets")
|
||||
@router.get(
|
||||
"/wallets", name="List all available wallets", response_model=WalletsResponse
|
||||
)
|
||||
async def wallets():
|
||||
wallets = [
|
||||
d for d in listdir(settings.cashu_dir) if isdir(join(settings.cashu_dir, d))
|
||||
@@ -371,38 +381,35 @@ async def wallets():
|
||||
)
|
||||
except:
|
||||
pass
|
||||
return result
|
||||
return WalletsResponse(wallets=result)
|
||||
|
||||
|
||||
@router.get("/info", name="Information about Cashu wallet")
|
||||
@router.get("/info", name="Information about Cashu wallet", response_model=InfoResponse)
|
||||
async def info():
|
||||
general = {
|
||||
"version": settings.version,
|
||||
"wallet": wallet.name,
|
||||
"debug": settings.debug,
|
||||
"cashu_dir": settings.cashu_dir,
|
||||
"mint_url": settings.mint_url,
|
||||
}
|
||||
if settings.env_file:
|
||||
general.update({"settings": settings.env_file})
|
||||
if settings.tor:
|
||||
general.update({"tor": settings.tor})
|
||||
if settings.nostr_private_key:
|
||||
try:
|
||||
client = NostrClient(private_key=settings.nostr_private_key, connect=False)
|
||||
general.update(
|
||||
{
|
||||
"nostr": {
|
||||
"public_key": client.private_key.bech32(),
|
||||
"relays": settings.nostr_relays,
|
||||
},
|
||||
}
|
||||
)
|
||||
nostr_public_key = client.private_key.bech32()
|
||||
nostr_relays = settings.nostr_relays
|
||||
except:
|
||||
general.update({"nostr": "Invalid key"})
|
||||
nostr_public_key = "Invalid key"
|
||||
nostr_relays = []
|
||||
else:
|
||||
nostr_public_key = None
|
||||
nostr_relays = []
|
||||
if settings.socks_host:
|
||||
general.update(
|
||||
{"socks proxy": settings.socks_host + ":" + str(settings.socks_host)}
|
||||
)
|
||||
|
||||
return general
|
||||
socks_proxy = settings.socks_host + ":" + str(settings.socks_host)
|
||||
else:
|
||||
socks_proxy = None
|
||||
return InfoResponse(
|
||||
version=settings.version,
|
||||
wallet=wallet.name,
|
||||
debug=settings.debug,
|
||||
cashu_dir=settings.cashu_dir,
|
||||
mint_url=settings.mint_url,
|
||||
settings=settings.env_file,
|
||||
tor=settings.tor,
|
||||
nostr_public_key=nostr_public_key,
|
||||
nostr_relays=nostr_relays,
|
||||
socks_proxy=socks_proxy,
|
||||
)
|
||||
|
||||
@@ -35,7 +35,6 @@ def test_pending():
|
||||
with TestClient(app) as client:
|
||||
response = client.get("/pending")
|
||||
assert response.status_code == 200
|
||||
assert response.json()["0"]
|
||||
|
||||
|
||||
def test_receive_all(mint):
|
||||
@@ -114,7 +113,7 @@ def test_flow(mint):
|
||||
response = client.post("/send?amount=50")
|
||||
assert response.json()["balance"] == initial_balance
|
||||
response = client.get("/pending")
|
||||
token = response.json()["0"]["token"]
|
||||
amount = response.json()["0"]["amount"]
|
||||
token = response.json()["pending_token"]["0"]["token"]
|
||||
amount = response.json()["pending_token"]["0"]["amount"]
|
||||
response = client.post(f"/receive?token={token}")
|
||||
assert response.json()["balance"] == initial_balance + amount
|
||||
|
||||
Reference in New Issue
Block a user