Merge pull request #663 from lnbits/lnurlp/float_fiat_amount

lnurlp: support for floating point fiat values
This commit is contained in:
Arc 2022-06-15 09:06:37 +01:00 committed by GitHub
commit 399ff11b04
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 36 additions and 13 deletions

View file

@ -9,6 +9,12 @@ async def create_pay_link(data: CreatePayLinkData, wallet_id: str) -> PayLink:
returning = "" if db.type == SQLITE else "RETURNING ID" returning = "" if db.type == SQLITE else "RETURNING ID"
method = db.execute if db.type == SQLITE else db.fetchone method = db.execute if db.type == SQLITE else db.fetchone
# database only allows int4 entries for min and max. For fiat currencies,
# we multiply by data.fiat_base_multiplier (usually 100) to save the value in cents.
if data.currency and data.fiat_base_multiplier:
data.min *= data.fiat_base_multiplier
data.max *= data.fiat_base_multiplier
result = await (method)( result = await (method)(
f""" f"""
INSERT INTO lnurlp.pay_links ( INSERT INTO lnurlp.pay_links (
@ -22,9 +28,10 @@ async def create_pay_link(data: CreatePayLinkData, wallet_id: str) -> PayLink:
success_text, success_text,
success_url, success_url,
comment_chars, comment_chars,
currency currency,
fiat_base_multiplier
) )
VALUES (?, ?, ?, ?, 0, 0, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, 0, 0, ?, ?, ?, ?, ?, ?)
{returning} {returning}
""", """,
( (
@ -37,6 +44,7 @@ async def create_pay_link(data: CreatePayLinkData, wallet_id: str) -> PayLink:
data.success_url, data.success_url,
data.comment_chars, data.comment_chars,
data.currency, data.currency,
data.fiat_base_multiplier,
), ),
) )
if db.type == SQLITE: if db.type == SQLITE:

View file

@ -29,11 +29,15 @@ async def api_lnurl_response(request: Request, link_id):
status_code=HTTPStatus.NOT_FOUND, detail="Pay link does not exist." status_code=HTTPStatus.NOT_FOUND, detail="Pay link does not exist."
) )
rate = await get_fiat_rate_satoshis(link.currency) if link.currency else 1 rate = (
await get_fiat_rate_satoshis(link.currency) / link.fiat_base_multiplier
if link.currency
else 1
)
resp = LnurlPayResponse( resp = LnurlPayResponse(
callback=request.url_for("lnurlp.api_lnurl_callback", link_id=link.id), callback=request.url_for("lnurlp.api_lnurl_callback", link_id=link.id),
min_sendable=math.ceil(link.min * rate) * 1000, min_sendable=round(link.min * rate) * 1000,
max_sendable=round(link.max * rate) * 1000, max_sendable=round(link.max * rate) * 1000,
metadata=link.lnurlpay_metadata, metadata=link.lnurlpay_metadata,
) )

View file

@ -50,3 +50,13 @@ async def m003_min_max_comment_fiat(db):
await db.execute("ALTER TABLE lnurlp.pay_links ADD COLUMN max INTEGER;") await db.execute("ALTER TABLE lnurlp.pay_links ADD COLUMN max INTEGER;")
await db.execute("UPDATE lnurlp.pay_links SET max = min;") await db.execute("UPDATE lnurlp.pay_links SET max = min;")
await db.execute("DROP TABLE lnurlp.invoices") await db.execute("DROP TABLE lnurlp.invoices")
async def m004_fiat_base_multiplier(db):
"""
Store the multiplier for fiat prices. We store the price in cents and
remember to multiply by 100 when we use it to convert to Dollars.
"""
await db.execute(
"ALTER TABLE lnurlp.pay_links ADD COLUMN fiat_base_multiplier INTEGER DEFAULT 1;"
)

View file

@ -11,20 +11,21 @@ from pydantic import BaseModel
class CreatePayLinkData(BaseModel): class CreatePayLinkData(BaseModel):
description: str description: str
min: int = Query(0.01, ge=0.01) min: float = Query(1, ge=0.01)
max: int = Query(0.01, ge=0.01) max: float = Query(1, ge=0.01)
currency: str = Query(None) currency: str = Query(None)
comment_chars: int = Query(0, ge=0, lt=800) comment_chars: int = Query(0, ge=0, lt=800)
webhook_url: str = Query(None) webhook_url: str = Query(None)
success_text: str = Query(None) success_text: str = Query(None)
success_url: str = Query(None) success_url: str = Query(None)
fiat_base_multiplier: int = Query(100, ge=1)
class PayLink(BaseModel): class PayLink(BaseModel):
id: int id: int
wallet: str wallet: str
description: str description: str
min: int min: float
served_meta: int served_meta: int
served_pr: int served_pr: int
webhook_url: Optional[str] webhook_url: Optional[str]
@ -32,11 +33,15 @@ class PayLink(BaseModel):
success_url: Optional[str] success_url: Optional[str]
currency: Optional[str] currency: Optional[str]
comment_chars: int comment_chars: int
max: int max: float
fiat_base_multiplier: int
@classmethod @classmethod
def from_row(cls, row: Row) -> "PayLink": def from_row(cls, row: Row) -> "PayLink":
data = dict(row) data = dict(row)
if data["currency"] and data["fiat_base_multiplier"]:
data["min"] /= data["fiat_base_multiplier"]
data["max"] /= data["fiat_base_multiplier"]
return cls(**data) return cls(**data)
def lnurl(self, req: Request) -> str: def lnurl(self, req: Request) -> str:

View file

@ -76,10 +76,6 @@ async def api_link_create_or_update(
link_id=None, link_id=None,
wallet: WalletTypeInfo = Depends(get_key_type), wallet: WalletTypeInfo = Depends(get_key_type),
): ):
if data.min < 1:
raise HTTPException(
detail="Min must be more than 1.", status_code=HTTPStatus.BAD_REQUEST
)
if data.min > data.max: if data.min > data.max:
raise HTTPException( raise HTTPException(
@ -87,7 +83,7 @@ async def api_link_create_or_update(
) )
if data.currency == None and ( if data.currency == None and (
round(data.min) != data.min or round(data.max) != data.max round(data.min) != data.min or round(data.max) != data.max or data.min < 1
): ):
raise HTTPException( raise HTTPException(
detail="Must use full satoshis.", status_code=HTTPStatus.BAD_REQUEST detail="Must use full satoshis.", status_code=HTTPStatus.BAD_REQUEST