Merge pull request #507 from lnbits/fakewallet-demo

Makes sats denomination customisable for FakeWallet
This commit is contained in:
Arc 2022-02-03 12:58:16 +00:00 committed by GitHub
commit 18ae455824
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 105 additions and 51 deletions

View File

@ -75,4 +75,5 @@ OPENNODE_API_ENDPOINT=https://api.opennode.com/
OPENNODE_KEY=OPENNODE_ADMIN_KEY OPENNODE_KEY=OPENNODE_ADMIN_KEY
# FakeWallet # FakeWallet
FAKE_WALLET_KEY=E9873D79C6D87DC0FB6A5778633389F4453213303DA61F20BD67FC233AA33262 FAKE_WALLET_SECRET="ToTheMoon1"
LNBITS_DENOMINATION=sats

View File

@ -161,14 +161,14 @@ new Vue({
{ {
name: 'sat', name: 'sat',
align: 'right', align: 'right',
label: 'Amount (sat)', label: 'Amount (' + LNBITS_DENOMINATION + ')',
field: 'sat', field: 'sat',
sortable: true sortable: true
}, },
{ {
name: 'fee', name: 'fee',
align: 'right', align: 'right',
label: 'Fee (msat)', label: 'Fee (m' + LNBITS_DENOMINATION + ')',
field: 'fee' field: 'fee'
} }
], ],
@ -191,7 +191,11 @@ new Vue({
}, },
computed: { computed: {
formattedBalance: function () { formattedBalance: function () {
return LNbits.utils.formatSat(this.balance || this.g.wallet.sat) if (LNBITS_DENOMINATION != 'sats') {
return this.balance / 100
} else {
return LNbits.utils.formatSat(this.balance || this.g.wallet.sat)
}
}, },
filteredPayments: function () { filteredPayments: function () {
var q = this.paymentsTable.filter var q = this.paymentsTable.filter
@ -250,28 +254,27 @@ new Vue({
this.parse.data.paymentChecker = null this.parse.data.paymentChecker = null
this.parse.camera.show = false this.parse.camera.show = false
}, },
updateBalance: function(credit){ updateBalance: function (credit) {
if (LNBITS_DENOMINATION != 'sats') {
credit = credit * 100
}
LNbits.api LNbits.api
.request( .request('PUT', '/api/v1/wallet/balance/' + credit, this.g.wallet.inkey)
'PUT', .catch(err => {
'/api/v1/wallet/balance/' + credit, LNbits.utils.notifyApiError(err)
this.g.wallet.inkey })
) .then(response => {
.catch(err => { let data = response.data
LNbits.utils.notifyApiError(err) if (data.status === 'ERROR') {
}) this.$q.notify({
.then(response => { timeout: 5000,
let data = response.data type: 'warning',
if (data.status === 'ERROR') { message: `Failed to update.`
this.$q.notify({ })
timeout: 5000, return
type: 'warning', }
message: `Failed to update.`, this.balance = this.balance + data.balance
}) })
return
}
this.balance = this.balance + data.balance
})
}, },
closeReceiveDialog: function () { closeReceiveDialog: function () {
setTimeout(() => { setTimeout(() => {
@ -295,7 +298,9 @@ new Vue({
}, },
createInvoice: function () { createInvoice: function () {
this.receive.status = 'loading' this.receive.status = 'loading'
if (LNBITS_DENOMINATION != 'sats') {
this.receive.data.amount = this.receive.data.amount * 100
}
LNbits.api LNbits.api
.createInvoice( .createInvoice(
this.g.wallet, this.g.wallet,

View File

@ -15,8 +15,8 @@
<q-card> <q-card>
<q-card-section> <q-card-section>
<h3 class="q-my-none"> <h3 class="q-my-none">
<strong>{% raw %}{{ formattedBalance }}{% endraw %} </strong> <strong>{% raw %}{{ formattedBalance }} {% endraw %}</strong>
sat {{LNBITS_DENOMINATION}}
<q-btn <q-btn
v-if="'{{user.admin}}' == 'True'" v-if="'{{user.admin}}' == 'True'"
flat flat
@ -35,7 +35,9 @@
v-model="scope.value" v-model="scope.value"
dense dense
autofocus autofocus
type="number" mask="#.##"
fill-mask="0"
reverse-fill-mask
@keyup.enter="updateBalance(scope.value)" @keyup.enter="updateBalance(scope.value)"
> >
<template v-slot:append> <template v-slot:append>
@ -169,7 +171,17 @@
<q-tooltip>{{ props.row.date }}</q-tooltip> <q-tooltip>{{ props.row.date }}</q-tooltip>
{{ props.row.dateFrom }} {{ props.row.dateFrom }}
</q-td> </q-td>
<q-td auto-width key="sat" :props="props"> {% endraw %}
<q-td
auto-width
key="sat"
v-if="'{{LNBITS_DENOMINATION}}' != 'sats'"
:props="props"
>{% raw %} {{ parseFloat(String(props.row.fsat).replaceAll(",",
"")) / 100 }}
</q-td>
<q-td auto-width key="sat" v-else :props="props">
{{ props.row.fsat }} {{ props.row.fsat }}
</q-td> </q-td>
<q-td auto-width key="fee" :props="props"> <q-td auto-width key="fee" :props="props">
@ -251,7 +263,7 @@
<q-card> <q-card>
<q-card-section> <q-card-section>
<h6 class="text-subtitle1 q-mt-none q-mb-sm"> <h6 class="text-subtitle1 q-mt-none q-mb-sm">
{{ SITE_TITLE }} wallet: <strong><em>{{ wallet.name }}</em></strong> {{ SITE_TITLE }} Wallet: <strong><em>{{ wallet.name }}</em></strong>
</h6> </h6>
</q-card-section> </q-card-section>
<q-card-section class="q-pa-none"> <q-card-section class="q-pa-none">
@ -365,7 +377,20 @@
<p v-if="receive.lnurl" class="text-h6 text-center q-my-none"> <p v-if="receive.lnurl" class="text-h6 text-center q-my-none">
<b>{{receive.lnurl.domain}}</b> is requesting an invoice: <b>{{receive.lnurl.domain}}</b> is requesting an invoice:
</p> </p>
{% endraw %} {% if LNBITS_DENOMINATION != 'sats' %}
<q-input
filled
dense
v-model.number="receive.data.amount"
label="Amount ({{LNBITS_DENOMINATION}}) *"
mask="#.##"
fill-mask="0"
reverse-fill-mask
:min="receive.minMax[0]"
:max="receive.minMax[1]"
:readonly="receive.lnurl && receive.lnurl.fixed"
></q-input>
{% else %}
<q-select <q-select
filled filled
dense dense
@ -379,22 +404,24 @@
dense dense
v-model.number="receive.data.amount" v-model.number="receive.data.amount"
type="number" type="number"
:label="`Amount (${receive.unit}) *`" label="Amount ({{LNBITS_DENOMINATION}}) *"
:step="receive.unit != 'sat' ? '0.001' : '1'" :step="receive.unit != 'sat' ? '0.001' : '1'"
:min="receive.minMax[0]" :min="receive.minMax[0]"
:max="receive.minMax[1]" :max="receive.minMax[1]"
:readonly="receive.lnurl && receive.lnurl.fixed" :readonly="receive.lnurl && receive.lnurl.fixed"
></q-input> ></q-input>
{% endif %}
<q-input <q-input
filled filled
dense dense
v-model.trim="receive.data.memo" v-model.trim="receive.data.memo"
label="Memo *" label="Memo"
placeholder="LNbits invoice"
></q-input> ></q-input>
{% raw %}
<div v-if="receive.status == 'pending'" class="row q-mt-lg"> <div v-if="receive.status == 'pending'" class="row q-mt-lg">
<q-btn <q-btn
unelevated unelevatedamountamount
color="primary" color="primary"
:disable="receive.data.memo == null || receive.data.amount == null || receive.data.amount <= 0" :disable="receive.data.memo == null || receive.data.amount == null || receive.data.amount <= 0"
type="submit" type="submit"
@ -438,8 +465,13 @@
<q-dialog v-model="parse.show" @hide="closeParseDialog"> <q-dialog v-model="parse.show" @hide="closeParseDialog">
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card"> <q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
<div v-if="parse.invoice"> <div v-if="parse.invoice">
{% raw %} <h6 v-if="'{{LNBITS_DENOMINATION}}' != 'sats'" class="q-my-none">
<h6 class="q-my-none">{{ parse.invoice.fsat }} sat</h6> {% raw %} {{ parseFloat(String(parse.invoice.fsat).replaceAll(",", ""))
/ 100 }} {% endraw %} {{LNBITS_DENOMINATION}} {% raw %}
</h6>
<h6 v-else class="q-my-none">
{{ parse.invoice.fsat }}{% endraw %} {{LNBITS_DENOMINATION}} {% raw %}
</h6>
<q-separator class="q-my-sm"></q-separator> <q-separator class="q-my-sm"></q-separator>
<p class="text-wrap"> <p class="text-wrap">
<strong>Description:</strong> {{ parse.invoice.description }}<br /> <strong>Description:</strong> {{ parse.invoice.description }}<br />
@ -489,7 +521,7 @@
<q-form @submit="payLnurl" class="q-gutter-md"> <q-form @submit="payLnurl" class="q-gutter-md">
<p v-if="parse.lnurlpay.fixed" class="q-my-none text-h6"> <p v-if="parse.lnurlpay.fixed" class="q-my-none text-h6">
<b>{{ parse.lnurlpay.domain }}</b> is requesting {{ <b>{{ parse.lnurlpay.domain }}</b> is requesting {{
parse.lnurlpay.maxSendable | msatoshiFormat }} sat parse.lnurlpay.maxSendable | msatoshiFormat }} {{LNBITS_DENOMINATION}}
<span v-if="parse.lnurlpay.commentAllowed > 0"> <span v-if="parse.lnurlpay.commentAllowed > 0">
<br /> <br />
and a {{parse.lnurlpay.commentAllowed}}-char comment and a {{parse.lnurlpay.commentAllowed}}-char comment
@ -499,7 +531,8 @@
<b>{{ parse.lnurlpay.targetUser || parse.lnurlpay.domain }}</b> is <b>{{ parse.lnurlpay.targetUser || parse.lnurlpay.domain }}</b> is
requesting <br /> requesting <br />
between <b>{{ parse.lnurlpay.minSendable | msatoshiFormat }}</b> and between <b>{{ parse.lnurlpay.minSendable | msatoshiFormat }}</b> and
<b>{{ parse.lnurlpay.maxSendable | msatoshiFormat }}</b> sat <b>{{ parse.lnurlpay.maxSendable | msatoshiFormat }}</b>
{% endraw %} {{LNBITS_DENOMINATION}} {% raw %}
<span v-if="parse.lnurlpay.commentAllowed > 0"> <span v-if="parse.lnurlpay.commentAllowed > 0">
<br /> <br />
and a {{parse.lnurlpay.commentAllowed}}-char comment and a {{parse.lnurlpay.commentAllowed}}-char comment
@ -516,16 +549,18 @@
</div> </div>
<div class="row"> <div class="row">
<div class="col"> <div class="col">
{% endraw %}
<q-input <q-input
filled filled
dense dense
v-model.number="parse.data.amount" v-model.number="parse.data.amount"
type="number" type="number"
label="Amount (sat) *" label="Amount ({{LNBITS_DENOMINATION}}) *"
:min="parse.lnurlpay.minSendable / 1000" :min="parse.lnurlpay.minSendable / 1000"
:max="parse.lnurlpay.maxSendable / 1000" :max="parse.lnurlpay.maxSendable / 1000"
:readonly="parse.lnurlpay.fixed" :readonly="parse.lnurlpay.fixed"
></q-input> ></q-input>
{% raw %}
</div> </div>
<div class="col-8 q-pl-md" v-if="parse.lnurlpay.commentAllowed > 0"> <div class="col-8 q-pl-md" v-if="parse.lnurlpay.commentAllowed > 0">
<q-input <q-input
@ -539,7 +574,9 @@
</div> </div>
</div> </div>
<div class="row q-mt-lg"> <div class="row q-mt-lg">
<q-btn unelevated color="primary" type="submit">Send satoshis</q-btn> <q-btn unelevated color="primary" type="submit"
>Send {{LNBITS_DENOMINATION}}</q-btn
>
<q-btn v-close-popup flat color="grey" class="q-ml-auto" <q-btn v-close-popup flat color="grey" class="q-ml-auto"
>Cancel</q-btn >Cancel</q-btn
> >

View File

@ -156,6 +156,7 @@ def template_renderer(additional_folders: List = []) -> Jinja2Templates:
) )
) )
t.env.globals["SITE_TITLE"] = settings.LNBITS_SITE_TITLE t.env.globals["SITE_TITLE"] = settings.LNBITS_SITE_TITLE
t.env.globals["LNBITS_DENOMINATION"] = settings.LNBITS_DENOMINATION
t.env.globals["SITE_TAGLINE"] = settings.LNBITS_SITE_TAGLINE t.env.globals["SITE_TAGLINE"] = settings.LNBITS_SITE_TAGLINE
t.env.globals["SITE_DESCRIPTION"] = settings.LNBITS_SITE_DESCRIPTION t.env.globals["SITE_DESCRIPTION"] = settings.LNBITS_SITE_DESCRIPTION
t.env.globals["LNBITS_THEME_OPTIONS"] = settings.LNBITS_THEME_OPTIONS t.env.globals["LNBITS_THEME_OPTIONS"] = settings.LNBITS_THEME_OPTIONS

View File

@ -34,6 +34,7 @@ LNBITS_DISABLED_EXTENSIONS: List[str] = env.list(
) )
LNBITS_SITE_TITLE = env.str("LNBITS_SITE_TITLE", default="LNbits") LNBITS_SITE_TITLE = env.str("LNBITS_SITE_TITLE", default="LNbits")
LNBITS_DENOMINATION = env.str("LNBITS_DENOMINATION", default="sats")
LNBITS_SITE_TAGLINE = env.str( LNBITS_SITE_TAGLINE = env.str(
"LNBITS_SITE_TAGLINE", default="free and open-source lightning wallet" "LNBITS_SITE_TAGLINE", default="free and open-source lightning wallet"
) )

View File

@ -22,7 +22,8 @@ Vue.component('lnbits-wallet-list', {
activeWallet: null, activeWallet: null,
activeBalance: [], activeBalance: [],
showForm: false, showForm: false,
walletName: '' walletName: '',
LNBITS_DENOMINATION: LNBITS_DENOMINATION
} }
}, },
template: ` template: `
@ -43,7 +44,8 @@ Vue.component('lnbits-wallet-list', {
</q-item-section> </q-item-section>
<q-item-section> <q-item-section>
<q-item-label lines="1">{{ wallet.name }}</q-item-label> <q-item-label lines="1">{{ wallet.name }}</q-item-label>
<q-item-label caption>{{ wallet.live_fsat }} sat</q-item-label> <q-item-label v-if="LNBITS_DENOMINATION != 'sats'" caption>{{ parseFloat(String(wallet.live_fsat).replaceAll(",", "")) / 100 }} {{ LNBITS_DENOMINATION }}</q-item-label>
<q-item-label v-else caption>{{ wallet.live_fsat }} {{ LNBITS_DENOMINATION }}</q-item-label>
</q-item-section> </q-item-section>
<q-item-section side v-show="activeWallet && activeWallet.id === wallet.id"> <q-item-section side v-show="activeWallet && activeWallet.id === wallet.id">
<q-icon name="chevron_right" color="grey-5" size="md"></q-icon> <q-icon name="chevron_right" color="grey-5" size="md"></q-icon>
@ -194,11 +196,11 @@ Vue.component('lnbits-payment-details', {
</div> </div>
<div class="row"> <div class="row">
<div class="col-3"><b>Amount</b>:</div> <div class="col-3"><b>Amount</b>:</div>
<div class="col-9">{{ (payment.amount / 1000).toFixed(3) }} sat</div> <div class="col-9">{{ (payment.amount / 1000).toFixed(3) }} {{LNBITS_DENOMINATION}}</div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-3"><b>Fee</b>:</div> <div class="col-3"><b>Fee</b>:</div>
<div class="col-9">{{ (payment.fee / 1000).toFixed(3) }} sat</div> <div class="col-9">{{ (payment.fee / 1000).toFixed(3) }} {{LNBITS_DENOMINATION}}</div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-3"><b>Payment hash</b>:</div> <div class="col-3"><b>Payment hash</b>:</div>

View File

@ -192,6 +192,8 @@
<script src="/static/js/components.js"></script> <script src="/static/js/components.js"></script>
<script type="text/javascript"> <script type="text/javascript">
const themes = {{ LNBITS_THEME_OPTIONS | tojson }} const themes = {{ LNBITS_THEME_OPTIONS | tojson }}
const LNBITS_DENOMINATION = {{ LNBITS_DENOMINATION | tojson}}
console.log(LNBITS_DENOMINATION)
if(themes && themes.length) { if(themes && themes.length) {
window.allowedThemes = themes.map(str => str.trim()) window.allowedThemes = themes.map(str => str.trim())
} }

View File

@ -31,11 +31,12 @@ class FakeWallet(Wallet):
memo: Optional[str] = None, memo: Optional[str] = None,
description_hash: Optional[bytes] = None, description_hash: Optional[bytes] = None,
) -> InvoiceResponse: ) -> InvoiceResponse:
secret = getenv("FAKE_WALLET_SECRET")
data: Dict = { data: Dict = {
"out": False, "out": False,
"amount": amount, "amount": amount,
"currency": "bc", "currency": "bc",
"privkey": getenv("FAKE_WALLET_KEY"), "privkey": hashlib.pbkdf2_hmac('sha256', secret.encode("utf-8"), ("FakeWallet").encode("utf-8"), 2048, 32).hex(),
"memo": None, "memo": None,
"description_hash": None, "description_hash": None,
"description": "", "description": "",
@ -52,9 +53,9 @@ class FakeWallet(Wallet):
data["tags_set"] = ["d"] data["tags_set"] = ["d"]
data["memo"] = memo data["memo"] = memo
data["description"] = memo data["description"] = memo
randomHash = hashlib.sha256( randomHash = data["privkey"][:6] + hashlib.sha256(
str(random.getrandbits(256)).encode("utf-8") str(random.getrandbits(256)).encode("utf-8")
).hexdigest() ).hexdigest()[6:]
data["paymenthash"] = randomHash data["paymenthash"] = randomHash
payment_request = encode(data) payment_request = encode(data)
checking_id = randomHash checking_id = randomHash
@ -62,8 +63,12 @@ class FakeWallet(Wallet):
return InvoiceResponse(True, checking_id, payment_request) return InvoiceResponse(True, checking_id, payment_request)
async def pay_invoice(self, bolt11: str) -> PaymentResponse: async def pay_invoice(self, bolt11: str) -> PaymentResponse:
invoice = decode(bolt11) invoice = decode(bolt11)
return PaymentResponse(True, invoice.payment_hash, 0) if hasattr(invoice, 'checking_id') and invoice.checking_id[6:] == data["privkey"][:6]:
return PaymentResponse(True, invoice.payment_hash, 0)
else:
return PaymentResponse(ok = False, error_message="Only internal invoices can be used!")
async def get_invoice_status(self, checking_id: str) -> PaymentStatus: async def get_invoice_status(self, checking_id: str) -> PaymentStatus:
return PaymentStatus(False) return PaymentStatus(False)