From 914b9f3ffe06432eb8623905ae74d92634ae826f Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Wed, 16 Feb 2022 22:23:26 +0100
Subject: [PATCH 1/8] allow amounts >0
---
lnbits/core/templates/core/_api_docs.html | 2 +-
lnbits/core/views/api.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/lnbits/core/templates/core/_api_docs.html b/lnbits/core/templates/core/_api_docs.html
index c7f3f9ade..0e74f38e4 100644
--- a/lnbits/core/templates/core/_api_docs.html
+++ b/lnbits/core/templates/core/_api_docs.html
@@ -61,7 +61,7 @@
curl -X POST {{ request.base_url }}api/v1/payments -d '{"out": false,
"amount": <int>, "memo": <string>, "webhook":
- <url:string>}' -H "X-Api-Key: {{ wallet.inkey }}" -H
+ <url:string>, "unit": <string>}' -H "X-Api-Key: {{ wallet.inkey }}" -H
"Content-type: application/json"
diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py
index 107a26846..a5358275c 100644
--- a/lnbits/core/views/api.py
+++ b/lnbits/core/views/api.py
@@ -123,7 +123,7 @@ async def api_payments(wallet: WalletTypeInfo = Depends(get_key_type)):
class CreateInvoiceData(BaseModel):
out: Optional[bool] = True
- amount: int = Query(None, ge=1)
+ amount: int = Query(None, ge=0)
memo: str = None
unit: Optional[str] = "sat"
description_hash: Optional[str] = None
From 30ab519c3a5bfc0485ca12e2f9b2b3c217b3aad2 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Wed, 16 Feb 2022 22:41:12 +0100
Subject: [PATCH 2/8] amount is float and sats are int
---
lnbits/core/views/api.py | 87 +++++++++++-----------------------------
1 file changed, 24 insertions(+), 63 deletions(-)
diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py
index a5358275c..e54c30d49 100644
--- a/lnbits/core/views/api.py
+++ b/lnbits/core/views/api.py
@@ -68,13 +68,9 @@ async def api_wallet(wallet: WalletTypeInfo = Depends(get_key_type)):
@core_app.put("/api/v1/wallet/balance/{amount}")
-async def api_update_balance(
- amount: int, wallet: WalletTypeInfo = Depends(get_key_type)
-):
+async def api_update_balance(amount: int, wallet: WalletTypeInfo = Depends(get_key_type)):
if wallet.wallet.user not in LNBITS_ADMIN_USERS:
- raise HTTPException(
- status_code=HTTPStatus.FORBIDDEN, detail="Not an admin user"
- )
+ raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail="Not an admin user")
payHash = urlsafe_short_hash()
await create_payment(
@@ -97,9 +93,7 @@ async def api_update_balance(
@core_app.put("/api/v1/wallet/{new_name}")
-async def api_update_wallet(
- new_name: str, wallet: WalletTypeInfo = Depends(WalletAdminKeyChecker())
-):
+async def api_update_wallet(new_name: str, wallet: WalletTypeInfo = Depends(WalletAdminKeyChecker())):
await update_wallet(wallet.wallet.id, new_name)
return {
"id": wallet.wallet.id,
@@ -111,19 +105,15 @@ async def api_update_wallet(
@core_app.get("/api/v1/payments")
async def api_payments(wallet: WalletTypeInfo = Depends(get_key_type)):
await get_payments(wallet_id=wallet.wallet.id, pending=True, complete=True)
- pendingPayments = await get_payments(
- wallet_id=wallet.wallet.id, pending=True, exclude_uncheckable=True
- )
+ pendingPayments = await get_payments(wallet_id=wallet.wallet.id, pending=True, exclude_uncheckable=True)
for payment in pendingPayments:
- await check_invoice_status(
- wallet_id=payment.wallet_id, payment_hash=payment.payment_hash
- )
+ await check_invoice_status(wallet_id=payment.wallet_id, payment_hash=payment.payment_hash)
return await get_payments(wallet_id=wallet.wallet.id, pending=True, complete=True)
class CreateInvoiceData(BaseModel):
out: Optional[bool] = True
- amount: int = Query(None, ge=0)
+ amount: float = Query(None, ge=0)
memo: str = None
unit: Optional[str] = "sat"
description_hash: Optional[str] = None
@@ -142,7 +132,7 @@ async def api_payments_create_invoice(data: CreateInvoiceData, wallet: Wallet):
description_hash = b""
memo = data.memo
if data.unit == "sat":
- amount = data.amount
+ amount = int(data.amount)
else:
price_in_sats = await fiat_amount_as_satoshis(data.amount, data.unit)
amount = price_in_sats
@@ -242,9 +232,7 @@ async def api_payments_create(
status_code=HTTPStatus.BAD_REQUEST,
detail="BOLT11 string is invalid or not given",
)
- return await api_payments_pay_invoice(
- invoiceData.bolt11, wallet.wallet
- ) # admin key
+ return await api_payments_pay_invoice(invoiceData.bolt11, wallet.wallet) # admin key
# invoice key
return await api_payments_create_invoice(invoiceData, wallet.wallet)
@@ -258,9 +246,7 @@ class CreateLNURLData(BaseModel):
@core_app.post("/api/v1/payments/lnurl")
-async def api_payments_pay_lnurl(
- data: CreateLNURLData, wallet: WalletTypeInfo = Depends(get_key_type)
-):
+async def api_payments_pay_lnurl(data: CreateLNURLData, wallet: WalletTypeInfo = Depends(get_key_type)):
domain = urlparse(data.callback).netloc
async with httpx.AsyncClient() as client:
@@ -292,11 +278,11 @@ async def api_payments_pay_lnurl(
detail=f"{domain} returned an invalid invoice. Expected {data.amount} msat, got {invoice.amount_msat}.",
)
- # if invoice.description_hash != data.description_hash:
- # raise HTTPException(
- # status_code=HTTPStatus.BAD_REQUEST,
- # detail=f"{domain} returned an invalid invoice. Expected description_hash == {data.description_hash}, got {invoice.description_hash}.",
- # )
+ # if invoice.description_hash != data.description_hash:
+ # raise HTTPException(
+ # status_code=HTTPStatus.BAD_REQUEST,
+ # detail=f"{domain} returned an invalid invoice. Expected description_hash == {data.description_hash}, got {invoice.description_hash}.",
+ # )
extra = {}
@@ -354,12 +340,8 @@ async def subscribe(request: Request, wallet: Wallet):
@core_app.get("/api/v1/payments/sse")
-async def api_payments_sse(
- request: Request, wallet: WalletTypeInfo = Depends(get_key_type)
-):
- return EventSourceResponse(
- subscribe(request, wallet), ping=20, media_type="text/event-stream"
- )
+async def api_payments_sse(request: Request, wallet: WalletTypeInfo = Depends(get_key_type)):
+ return EventSourceResponse(subscribe(request, wallet), ping=20, media_type="text/event-stream")
@core_app.get("/api/v1/payments/{payment_hash}")
@@ -368,9 +350,7 @@ async def api_payment(payment_hash):
await check_invoice_status(payment.wallet_id, payment_hash)
payment = await get_standalone_payment(payment_hash)
if not payment:
- raise HTTPException(
- status_code=HTTPStatus.NOT_FOUND, detail="Payment does not exist."
- )
+ raise HTTPException(status_code=HTTPStatus.NOT_FOUND, detail="Payment does not exist.")
elif not payment.pending:
return {"paid": True, "preimage": payment.preimage}
@@ -382,9 +362,7 @@ async def api_payment(payment_hash):
return {"paid": not payment.pending, "preimage": payment.preimage}
-@core_app.get(
- "/api/v1/lnurlscan/{code}", dependencies=[Depends(WalletInvoiceKeyChecker())]
-)
+@core_app.get("/api/v1/lnurlscan/{code}", dependencies=[Depends(WalletInvoiceKeyChecker())])
async def api_lnurlscan(code: str):
try:
url = lnurl.decode(code)
@@ -394,17 +372,10 @@ async def api_lnurlscan(code: str):
name_domain = code.split("@")
if len(name_domain) == 2 and len(name_domain[1].split(".")) == 2:
name, domain = name_domain
- url = (
- ("http://" if domain.endswith(".onion") else "https://")
- + domain
- + "/.well-known/lnurlp/"
- + name
- )
+ url = ("http://" if domain.endswith(".onion") else "https://") + domain + "/.well-known/lnurlp/" + name
# will proceed with these values
else:
- raise HTTPException(
- status_code=HTTPStatus.BAD_REQUEST, detail="invalid lnurl"
- )
+ raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail="invalid lnurl")
# params is what will be returned to the client
params: Dict = {"domain": domain}
@@ -463,20 +434,14 @@ async def api_lnurlscan(code: str):
params.update(balanceCheck=data["balanceCheck"])
# format callback url and send to client
- parsed_callback = parsed_callback._replace(
- query=urlencode(qs, doseq=True)
- )
+ parsed_callback = parsed_callback._replace(query=urlencode(qs, doseq=True))
params.update(callback=urlunparse(parsed_callback))
if tag == "payRequest":
params.update(kind="pay")
params.update(fixed=data["minSendable"] == data["maxSendable"])
- params.update(
- description_hash=hashlib.sha256(
- data["metadata"].encode("utf-8")
- ).hexdigest()
- )
+ params.update(description_hash=hashlib.sha256(data["metadata"].encode("utf-8")).hexdigest())
metadata = json.loads(data["metadata"])
for [k, v] in metadata:
if k == "text/plain":
@@ -528,9 +493,7 @@ async def api_payments_decode(data: str = Query(None)):
async def api_perform_lnurlauth(callback: str):
err = await perform_lnurlauth(callback)
if err:
- raise HTTPException(
- status_code=HTTPStatus.SERVICE_UNAVAILABLE, detail=err.reason
- )
+ raise HTTPException(status_code=HTTPStatus.SERVICE_UNAVAILABLE, detail=err.reason)
return ""
@@ -553,9 +516,7 @@ async def api_fiat_as_sats(data: ConversionData):
output["sats"] = int(data.amount)
output["BTC"] = data.amount / 100000000
for currency in data.to.split(","):
- output[currency.strip().upper()] = await satoshis_amount_as_fiat(
- data.amount, currency.strip()
- )
+ output[currency.strip().upper()] = await satoshis_amount_as_fiat(data.amount, currency.strip())
return output
else:
output[data.from_.upper()] = data.amount
From 9fb30080abf9068e58ae4b5ee9bab0d3c90c54e7 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Wed, 16 Feb 2022 22:42:27 +0100
Subject: [PATCH 3/8] black
---
lnbits/core/views/api.py | 73 ++++++++++++++++++++++++++++++----------
1 file changed, 56 insertions(+), 17 deletions(-)
diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py
index e54c30d49..392575202 100644
--- a/lnbits/core/views/api.py
+++ b/lnbits/core/views/api.py
@@ -68,9 +68,13 @@ async def api_wallet(wallet: WalletTypeInfo = Depends(get_key_type)):
@core_app.put("/api/v1/wallet/balance/{amount}")
-async def api_update_balance(amount: int, wallet: WalletTypeInfo = Depends(get_key_type)):
+async def api_update_balance(
+ amount: int, wallet: WalletTypeInfo = Depends(get_key_type)
+):
if wallet.wallet.user not in LNBITS_ADMIN_USERS:
- raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail="Not an admin user")
+ raise HTTPException(
+ status_code=HTTPStatus.FORBIDDEN, detail="Not an admin user"
+ )
payHash = urlsafe_short_hash()
await create_payment(
@@ -93,7 +97,9 @@ async def api_update_balance(amount: int, wallet: WalletTypeInfo = Depends(get_k
@core_app.put("/api/v1/wallet/{new_name}")
-async def api_update_wallet(new_name: str, wallet: WalletTypeInfo = Depends(WalletAdminKeyChecker())):
+async def api_update_wallet(
+ new_name: str, wallet: WalletTypeInfo = Depends(WalletAdminKeyChecker())
+):
await update_wallet(wallet.wallet.id, new_name)
return {
"id": wallet.wallet.id,
@@ -105,9 +111,13 @@ async def api_update_wallet(new_name: str, wallet: WalletTypeInfo = Depends(Wall
@core_app.get("/api/v1/payments")
async def api_payments(wallet: WalletTypeInfo = Depends(get_key_type)):
await get_payments(wallet_id=wallet.wallet.id, pending=True, complete=True)
- pendingPayments = await get_payments(wallet_id=wallet.wallet.id, pending=True, exclude_uncheckable=True)
+ pendingPayments = await get_payments(
+ wallet_id=wallet.wallet.id, pending=True, exclude_uncheckable=True
+ )
for payment in pendingPayments:
- await check_invoice_status(wallet_id=payment.wallet_id, payment_hash=payment.payment_hash)
+ await check_invoice_status(
+ wallet_id=payment.wallet_id, payment_hash=payment.payment_hash
+ )
return await get_payments(wallet_id=wallet.wallet.id, pending=True, complete=True)
@@ -232,7 +242,9 @@ async def api_payments_create(
status_code=HTTPStatus.BAD_REQUEST,
detail="BOLT11 string is invalid or not given",
)
- return await api_payments_pay_invoice(invoiceData.bolt11, wallet.wallet) # admin key
+ return await api_payments_pay_invoice(
+ invoiceData.bolt11, wallet.wallet
+ ) # admin key
# invoice key
return await api_payments_create_invoice(invoiceData, wallet.wallet)
@@ -246,7 +258,9 @@ class CreateLNURLData(BaseModel):
@core_app.post("/api/v1/payments/lnurl")
-async def api_payments_pay_lnurl(data: CreateLNURLData, wallet: WalletTypeInfo = Depends(get_key_type)):
+async def api_payments_pay_lnurl(
+ data: CreateLNURLData, wallet: WalletTypeInfo = Depends(get_key_type)
+):
domain = urlparse(data.callback).netloc
async with httpx.AsyncClient() as client:
@@ -340,8 +354,12 @@ async def subscribe(request: Request, wallet: Wallet):
@core_app.get("/api/v1/payments/sse")
-async def api_payments_sse(request: Request, wallet: WalletTypeInfo = Depends(get_key_type)):
- return EventSourceResponse(subscribe(request, wallet), ping=20, media_type="text/event-stream")
+async def api_payments_sse(
+ request: Request, wallet: WalletTypeInfo = Depends(get_key_type)
+):
+ return EventSourceResponse(
+ subscribe(request, wallet), ping=20, media_type="text/event-stream"
+ )
@core_app.get("/api/v1/payments/{payment_hash}")
@@ -350,7 +368,9 @@ async def api_payment(payment_hash):
await check_invoice_status(payment.wallet_id, payment_hash)
payment = await get_standalone_payment(payment_hash)
if not payment:
- raise HTTPException(status_code=HTTPStatus.NOT_FOUND, detail="Payment does not exist.")
+ raise HTTPException(
+ status_code=HTTPStatus.NOT_FOUND, detail="Payment does not exist."
+ )
elif not payment.pending:
return {"paid": True, "preimage": payment.preimage}
@@ -362,7 +382,9 @@ async def api_payment(payment_hash):
return {"paid": not payment.pending, "preimage": payment.preimage}
-@core_app.get("/api/v1/lnurlscan/{code}", dependencies=[Depends(WalletInvoiceKeyChecker())])
+@core_app.get(
+ "/api/v1/lnurlscan/{code}", dependencies=[Depends(WalletInvoiceKeyChecker())]
+)
async def api_lnurlscan(code: str):
try:
url = lnurl.decode(code)
@@ -372,10 +394,17 @@ async def api_lnurlscan(code: str):
name_domain = code.split("@")
if len(name_domain) == 2 and len(name_domain[1].split(".")) == 2:
name, domain = name_domain
- url = ("http://" if domain.endswith(".onion") else "https://") + domain + "/.well-known/lnurlp/" + name
+ url = (
+ ("http://" if domain.endswith(".onion") else "https://")
+ + domain
+ + "/.well-known/lnurlp/"
+ + name
+ )
# will proceed with these values
else:
- raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail="invalid lnurl")
+ raise HTTPException(
+ status_code=HTTPStatus.BAD_REQUEST, detail="invalid lnurl"
+ )
# params is what will be returned to the client
params: Dict = {"domain": domain}
@@ -434,14 +463,20 @@ async def api_lnurlscan(code: str):
params.update(balanceCheck=data["balanceCheck"])
# format callback url and send to client
- parsed_callback = parsed_callback._replace(query=urlencode(qs, doseq=True))
+ parsed_callback = parsed_callback._replace(
+ query=urlencode(qs, doseq=True)
+ )
params.update(callback=urlunparse(parsed_callback))
if tag == "payRequest":
params.update(kind="pay")
params.update(fixed=data["minSendable"] == data["maxSendable"])
- params.update(description_hash=hashlib.sha256(data["metadata"].encode("utf-8")).hexdigest())
+ params.update(
+ description_hash=hashlib.sha256(
+ data["metadata"].encode("utf-8")
+ ).hexdigest()
+ )
metadata = json.loads(data["metadata"])
for [k, v] in metadata:
if k == "text/plain":
@@ -493,7 +528,9 @@ async def api_payments_decode(data: str = Query(None)):
async def api_perform_lnurlauth(callback: str):
err = await perform_lnurlauth(callback)
if err:
- raise HTTPException(status_code=HTTPStatus.SERVICE_UNAVAILABLE, detail=err.reason)
+ raise HTTPException(
+ status_code=HTTPStatus.SERVICE_UNAVAILABLE, detail=err.reason
+ )
return ""
@@ -516,7 +553,9 @@ async def api_fiat_as_sats(data: ConversionData):
output["sats"] = int(data.amount)
output["BTC"] = data.amount / 100000000
for currency in data.to.split(","):
- output[currency.strip().upper()] = await satoshis_amount_as_fiat(data.amount, currency.strip())
+ output[currency.strip().upper()] = await satoshis_amount_as_fiat(
+ data.amount, currency.strip()
+ )
return output
else:
output[data.from_.upper()] = data.amount
From cd846233050388538f5dfacf1c57b7610c9abed9 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Thu, 17 Feb 2022 09:14:07 +0100
Subject: [PATCH 4/8] amount label adaptive to uni - look at me Im a frontent
dev now
---
lnbits/core/templates/core/wallet.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnbits/core/templates/core/wallet.html b/lnbits/core/templates/core/wallet.html
index 2b6ec5dec..68db58633 100644
--- a/lnbits/core/templates/core/wallet.html
+++ b/lnbits/core/templates/core/wallet.html
@@ -418,7 +418,7 @@
dense
v-model.number="receive.data.amount"
type="number"
- label="Amount ({{LNBITS_DENOMINATION}}) *"
+ :label="'Amount (' + receive.unit + ') *'"
:step="receive.unit != 'sat' ? '0.001' : '1'"
:min="receive.minMax[0]"
:max="receive.minMax[1]"
From d559c604fa841716c01c5f95ee8a3c589724971f Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Thu, 17 Feb 2022 09:29:10 +0100
Subject: [PATCH 5/8] invoice without memo
---
lnbits/core/views/api.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py
index 392575202..e353b03b6 100644
--- a/lnbits/core/views/api.py
+++ b/lnbits/core/views/api.py
@@ -140,7 +140,7 @@ async def api_payments_create_invoice(data: CreateInvoiceData, wallet: Wallet):
memo = ""
else:
description_hash = b""
- memo = data.memo
+ memo = data.memo if data.memo is not None else "LNbits"
if data.unit == "sat":
amount = int(data.amount)
else:
From 15c85574596dfd9cf12237e7bc9bbaf97bd6ad50 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Thu, 17 Feb 2022 10:16:11 +0100
Subject: [PATCH 6/8] allow empty memo
---
lnbits/core/templates/core/wallet.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnbits/core/templates/core/wallet.html b/lnbits/core/templates/core/wallet.html
index 68db58633..c7dc6fded 100644
--- a/lnbits/core/templates/core/wallet.html
+++ b/lnbits/core/templates/core/wallet.html
@@ -437,7 +437,7 @@
From 13a72d9a41abe3dc378c6c8c28203113b06c9c47 Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Thu, 17 Feb 2022 11:42:08 +0100
Subject: [PATCH 7/8] replace string with LNBITS_SITE_TITLE
---
lnbits/core/views/api.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py
index e353b03b6..d607e1492 100644
--- a/lnbits/core/views/api.py
+++ b/lnbits/core/views/api.py
@@ -31,6 +31,7 @@ from lnbits.utils.exchange_rates import (
fiat_amount_as_satoshis,
satoshis_amount_as_fiat,
)
+from lnbits.settings import LNBITS_SITE_TITLE
from .. import core_app, db
from ..crud import (
@@ -140,7 +141,7 @@ async def api_payments_create_invoice(data: CreateInvoiceData, wallet: Wallet):
memo = ""
else:
description_hash = b""
- memo = data.memo if data.memo is not None else "LNbits"
+ memo = data.memo or LNBITS_SITE_TITLE
if data.unit == "sat":
amount = int(data.amount)
else:
From 6496fac35fc9a160a91ba5ec14b907ae43e8408b Mon Sep 17 00:00:00 2001
From: callebtc <93376500+callebtc@users.noreply.github.com>
Date: Fri, 18 Feb 2022 12:35:25 +0100
Subject: [PATCH 8/8] fill mask for fiat
---
lnbits/core/templates/core/wallet.html | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/lnbits/core/templates/core/wallet.html b/lnbits/core/templates/core/wallet.html
index c7dc6fded..95436f860 100644
--- a/lnbits/core/templates/core/wallet.html
+++ b/lnbits/core/templates/core/wallet.html
@@ -417,9 +417,11 @@
filled
dense
v-model.number="receive.data.amount"
- type="number"
:label="'Amount (' + receive.unit + ') *'"
- :step="receive.unit != 'sat' ? '0.001' : '1'"
+ :mask="receive.unit != 'sat' ? '#.##' : '#'"
+ fill-mask="0"
+ reverse-fill-mask
+ :step="receive.unit != 'sat' ? '0.01' : '1'"
:min="receive.minMax[0]"
:max="receive.minMax[1]"
:readonly="receive.lnurl && receive.lnurl.fixed"