Compare commits
13
Commits
v1.4.0-rc2
...
v1.4.0-rc3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d098e2f710 | ||
|
|
a0f65f4cda | ||
|
|
606ee215b4 | ||
|
|
7716f819f6 | ||
|
|
8325d880cb | ||
|
|
b090470fc1 | ||
|
|
c7297d2e77 | ||
|
|
b6e111b21c | ||
|
|
7747d7b741 | ||
|
|
f3a5a8e002 | ||
|
|
157a6485b4 | ||
|
|
8867b27b09 | ||
|
|
68b607ecbc |
@@ -23,3 +23,10 @@ mypy.ini
|
|||||||
package-lock.json
|
package-lock.json
|
||||||
package.json
|
package.json
|
||||||
pytest.ini
|
pytest.ini
|
||||||
|
|
||||||
|
.mypy_cache
|
||||||
|
.github
|
||||||
|
.pytest_cache
|
||||||
|
.vscode
|
||||||
|
bin
|
||||||
|
dist
|
||||||
|
|||||||
@@ -10,7 +10,29 @@ permissions:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
|
release:
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
outputs:
|
||||||
|
upload_url: ${{ steps.get_upload_url.outputs.upload_url }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Create github pre-release
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
tag: ${{ github.ref_name }}
|
||||||
|
run: |
|
||||||
|
gh release create "$tag" --prerelease --generate-notes --draft
|
||||||
|
- id: get_upload_url
|
||||||
|
name: Get upload url of Github release
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
tag: ${{ github.ref_name }}
|
||||||
|
run: |
|
||||||
|
upload_url=$(gh release view "$tag" --json uploadUrl -q ".uploadUrl")
|
||||||
|
echo "upload_url=$upload_url" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
docker:
|
docker:
|
||||||
|
if: github.repository == 'lnbits/lnbits'
|
||||||
uses: ./.github/workflows/docker.yml
|
uses: ./.github/workflows/docker.yml
|
||||||
with:
|
with:
|
||||||
tag: ${{ github.ref_name }}
|
tag: ${{ github.ref_name }}
|
||||||
@@ -19,6 +41,7 @@ jobs:
|
|||||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
|
||||||
pypi:
|
pypi:
|
||||||
|
if: github.repository == 'lnbits/lnbits'
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- name: Install dependencies for building secp256k1
|
- name: Install dependencies for building secp256k1
|
||||||
@@ -30,3 +53,10 @@ jobs:
|
|||||||
uses: JRubics/poetry-publish@v1.15
|
uses: JRubics/poetry-publish@v1.15
|
||||||
with:
|
with:
|
||||||
pypi_token: ${{ secrets.PYPI_API_KEY }}
|
pypi_token: ${{ secrets.PYPI_API_KEY }}
|
||||||
|
|
||||||
|
appimage:
|
||||||
|
needs: [ release ]
|
||||||
|
uses: ./.github/workflows/appimage.yml
|
||||||
|
with:
|
||||||
|
tag_name: ${{ github.ref_name }}
|
||||||
|
upload_url: ${{ needs.release.outputs.upload_url }}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ jobs:
|
|||||||
release:
|
release:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
outputs:
|
outputs:
|
||||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
upload_url: ${{ steps.get_upload_url.outputs.upload_url }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Create github release
|
- name: Create github release
|
||||||
@@ -22,10 +22,18 @@ jobs:
|
|||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
tag: ${{ github.ref_name }}
|
tag: ${{ github.ref_name }}
|
||||||
run: |
|
run: |
|
||||||
upload_url=$(gh release create "$tag" --generate-notes --draft --json upload_url -q '.upload_url')
|
gh release create "$tag" --generate-notes --draft
|
||||||
|
- id: get_upload_url
|
||||||
|
name: Get upload url of Github release
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
tag: ${{ github.ref_name }}
|
||||||
|
run: |
|
||||||
|
upload_url=$(gh release view "$tag" --json uploadUrl -q ".uploadUrl")
|
||||||
echo "upload_url=$upload_url" >> "$GITHUB_OUTPUT"
|
echo "upload_url=$upload_url" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
docker:
|
docker:
|
||||||
|
if: github.repository == 'lnbits/lnbits'
|
||||||
needs: [ release ]
|
needs: [ release ]
|
||||||
uses: ./.github/workflows/docker.yml
|
uses: ./.github/workflows/docker.yml
|
||||||
with:
|
with:
|
||||||
@@ -35,6 +43,7 @@ jobs:
|
|||||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
|
||||||
docker-latest:
|
docker-latest:
|
||||||
|
if: github.repository == 'lnbits/lnbits'
|
||||||
needs: [ release ]
|
needs: [ release ]
|
||||||
uses: ./.github/workflows/docker.yml
|
uses: ./.github/workflows/docker.yml
|
||||||
with:
|
with:
|
||||||
@@ -44,6 +53,7 @@ jobs:
|
|||||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
|
||||||
pypi:
|
pypi:
|
||||||
|
if: github.repository == 'lnbits/lnbits'
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- name: Install dependencies for building secp256k1
|
- name: Install dependencies for building secp256k1
|
||||||
|
|||||||
@@ -138,7 +138,9 @@ def register_exception_handlers(app: FastAPI): # noqa: C901
|
|||||||
|
|
||||||
@app.exception_handler(404)
|
@app.exception_handler(404)
|
||||||
async def error_handler_404(request: Request, exc: HTTPException):
|
async def error_handler_404(request: Request, exc: HTTPException):
|
||||||
logger.error(f"404: {request.url.path} {exc.status_code}: {exc.detail}")
|
|
||||||
|
if not request.url.path.endswith("routes.json"):
|
||||||
|
logger.error(f"404: {request.url.path} {exc.status_code}: {exc.detail}")
|
||||||
|
|
||||||
if not _is_browser_request(request):
|
if not _is_browser_request(request):
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
|
|||||||
@@ -601,7 +601,6 @@ class BreezLiquidSdkFundingSource(LNbitsSettings):
|
|||||||
class BoltzFundingSource(LNbitsSettings):
|
class BoltzFundingSource(LNbitsSettings):
|
||||||
boltz_client_endpoint: str | None = Field(default="127.0.0.1:9002")
|
boltz_client_endpoint: str | None = Field(default="127.0.0.1:9002")
|
||||||
boltz_client_macaroon: str | None = Field(default=None)
|
boltz_client_macaroon: str | None = Field(default=None)
|
||||||
boltz_client_wallet: str | None = Field(default="lnbits")
|
|
||||||
boltz_client_password: str = Field(default="")
|
boltz_client_password: str = Field(default="")
|
||||||
boltz_client_cert: str | None = Field(default=None)
|
boltz_client_cert: str | None = Field(default=None)
|
||||||
boltz_mnemonic: str | None = Field(default=None)
|
boltz_mnemonic: str | None = Field(default=None)
|
||||||
|
|||||||
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
@@ -217,6 +217,7 @@ body.body--dark .q-header,
|
|||||||
body.body--dark .q-drawer {
|
body.body--dark .q-drawer {
|
||||||
--q-dark: rgba(29, 29, 29, 0.3);
|
--q-dark: rgba(29, 29, 29, 0.3);
|
||||||
background-color: var(--q-dark);
|
background-color: var(--q-dark);
|
||||||
|
backdrop-filter: blur(6px) brightness(0.8);
|
||||||
}
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
|
|||||||
@@ -163,11 +163,9 @@ window.app.component('lnbits-admin-funding-sources', {
|
|||||||
boltz_client_endpoint: 'Endpoint',
|
boltz_client_endpoint: 'Endpoint',
|
||||||
boltz_client_macaroon: 'Admin Macaroon path or hex',
|
boltz_client_macaroon: 'Admin Macaroon path or hex',
|
||||||
boltz_client_cert: 'Certificate path or hex',
|
boltz_client_cert: 'Certificate path or hex',
|
||||||
boltz_client_wallet: 'Wallet Name',
|
|
||||||
boltz_client_password: 'Wallet Password (can be empty)',
|
boltz_client_password: 'Wallet Password (can be empty)',
|
||||||
boltz_mnemonic: {
|
boltz_mnemonic: {
|
||||||
label: 'Liquid mnemonic (copy into greenwallet)',
|
label: 'Liquid mnemonic (copy into greenwallet)',
|
||||||
readonly: true,
|
|
||||||
copy: true,
|
copy: true,
|
||||||
qrcode: true
|
qrcode: true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,10 @@ window.app.component('lnbits-qrcode', {
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
|
print: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
showButtons: {
|
showButtons: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true
|
default: true
|
||||||
@@ -41,6 +45,41 @@ window.app.component('lnbits-qrcode', {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
printQrCode() {
|
||||||
|
const svg = this.$refs.qrCode.$el.outerHTML
|
||||||
|
const printWindow = window.open('', '_blank')
|
||||||
|
printWindow.document.write(`
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Print QR Code</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100vh;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
img {
|
||||||
|
position: absolute;
|
||||||
|
max-width: 51px;
|
||||||
|
width: 14%;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #fff;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: -1.2rem;
|
||||||
|
border-radius: -1.2rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>${svg}<img src="${this.logo}"></body>
|
||||||
|
</html>
|
||||||
|
`)
|
||||||
|
printWindow.document.close()
|
||||||
|
printWindow.focus()
|
||||||
|
printWindow.print()
|
||||||
|
printWindow.close()
|
||||||
|
},
|
||||||
clickQrCode(event) {
|
clickQrCode(event) {
|
||||||
if (this.href === '') {
|
if (this.href === '') {
|
||||||
this.utils.copyText(this.value)
|
this.utils.copyText(this.value)
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ const DynamicComponent = {
|
|||||||
let route = RENDERED_ROUTE
|
let route = RENDERED_ROUTE
|
||||||
// append trailing slash only on the root path `/path` -> `/path/`
|
// append trailing slash only on the root path `/path` -> `/path/`
|
||||||
if (route.split('/').length === 2) route += '/'
|
if (route.split('/').length === 2) route += '/'
|
||||||
if (route !== this.$route.fullPath) {
|
if (route !== this.$route.path) {
|
||||||
console.log('Redirecting to non-vue route:', this.$route.fullPath)
|
console.log('Redirecting to non-vue route:', this.$route.fullPath)
|
||||||
window.location = this.$route.fullPath
|
window.location = this.$route.fullPath
|
||||||
return
|
return
|
||||||
@@ -138,6 +138,9 @@ window.router = VueRouter.createRouter({
|
|||||||
routes
|
routes
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// BACKWARDS compatibility extensions
|
||||||
|
window.LOCALE = window.g.locale
|
||||||
|
|
||||||
window.i18n = new VueI18n.createI18n({
|
window.i18n = new VueI18n.createI18n({
|
||||||
locale: window.g.locale,
|
locale: window.g.locale,
|
||||||
fallbackLocale: 'en',
|
fallbackLocale: 'en',
|
||||||
|
|||||||
@@ -63,5 +63,6 @@ body.body--dark {
|
|||||||
.q-drawer {
|
.q-drawer {
|
||||||
--q-dark: #{color.adjust(#1d1d1d, $alpha: -0.7)};
|
--q-dark: #{color.adjust(#1d1d1d, $alpha: -0.7)};
|
||||||
background-color: var(--q-dark);
|
background-color: var(--q-dark);
|
||||||
|
backdrop-filter: blur(6px) brightness(0.8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -534,7 +534,7 @@ include('components/lnbits-error.vue') %}
|
|||||||
<q-tab name="bech32" icon="qr_code" label="bech32"></q-tab>
|
<q-tab name="bech32" icon="qr_code" label="bech32"></q-tab>
|
||||||
<q-tab name="lud17" icon="link" label="url (lud17)"></q-tab>
|
<q-tab name="lud17" icon="link" label="url (lud17)"></q-tab>
|
||||||
</q-tabs>
|
</q-tabs>
|
||||||
<lnbits-qrcode :value="lnurl" nfc="true"></lnbits-qrcode>
|
<lnbits-qrcode :value="lnurl" nfc="true" print="true"></lnbits-qrcode>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
@click="g.visibleDrawer = !g.visibleDrawer"
|
@click="g.visibleDrawer = !g.visibleDrawer"
|
||||||
></q-btn>
|
></q-btn>
|
||||||
<q-toolbar-title>
|
<q-toolbar-title>
|
||||||
<q-btn flat no-caps dense size="lg" type="a" href="/">
|
<q-btn flat no-caps dense class="q-mr-sm" size="lg" type="a" href="/">
|
||||||
<q-img
|
<q-img
|
||||||
v-if="customLogoUrl"
|
v-if="customLogoUrl"
|
||||||
height="30px"
|
height="30px"
|
||||||
@@ -73,32 +73,36 @@
|
|||||||
|
|
||||||
<lnbits-language-dropdown></lnbits-language-dropdown>
|
<lnbits-language-dropdown></lnbits-language-dropdown>
|
||||||
|
|
||||||
<q-btn-dropdown
|
<q-btn-dropdown v-if="g.user" flat rounded size="sm" class="q-pl-sm">
|
||||||
v-if="g.user || g.isUserAuthorized"
|
|
||||||
flat
|
|
||||||
rounded
|
|
||||||
size="sm"
|
|
||||||
class="q-pl-sm"
|
|
||||||
>
|
|
||||||
<template v-slot:label>
|
<template v-slot:label>
|
||||||
<q-avatar v-if="g.user?.extra?.picture !== ''" size="18px">
|
<q-avatar
|
||||||
|
v-if="g.user?.extra?.picture && g.user?.extra?.picture !== ''"
|
||||||
|
size="18px"
|
||||||
|
>
|
||||||
<q-img :src="g.user?.extra?.picture"></q-img>
|
<q-img :src="g.user?.extra?.picture"></q-img>
|
||||||
</q-avatar>
|
</q-avatar>
|
||||||
<q-avatar v-else icon="account_circle" size="18px"></q-avatar>
|
<q-avatar v-else icon="account_circle" size="18px"></q-avatar>
|
||||||
</template>
|
</template>
|
||||||
<q-list style="max-width: 200px">
|
<q-list style="max-width: 200px">
|
||||||
<q-item>
|
<q-item>
|
||||||
<q-item-section avatar v-if="g.user?.extra?.picture !== ''">
|
<q-item-section
|
||||||
|
avatar
|
||||||
|
v-if="
|
||||||
|
g.user &&
|
||||||
|
g.user?.extra?.picture &&
|
||||||
|
g.user?.extra?.picture !== ''
|
||||||
|
"
|
||||||
|
>
|
||||||
<q-avatar size="md">
|
<q-avatar size="md">
|
||||||
<img :src="g.user?.extra?.picture" />
|
<img :src="g.user?.extra?.picture" />
|
||||||
</q-avatar>
|
</q-avatar>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label
|
<q-item-label
|
||||||
|
caption
|
||||||
class="ellipsis"
|
class="ellipsis"
|
||||||
v-text="displayName"
|
v-text="displayName"
|
||||||
></q-item-label>
|
></q-item-label>
|
||||||
<q-item-label caption v-text="displayRole"></q-item-label>
|
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-separator></q-separator>
|
<q-separator></q-separator>
|
||||||
|
|||||||
@@ -44,6 +44,16 @@
|
|||||||
>
|
>
|
||||||
<q-tooltip>Write NFC Tag</q-tooltip>
|
<q-tooltip>Write NFC Tag</q-tooltip>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
|
<q-btn
|
||||||
|
v-if="print"
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
class="text-grey"
|
||||||
|
@click="printQrCode()"
|
||||||
|
icon="print"
|
||||||
|
>
|
||||||
|
<q-tooltip>Print</q-tooltip>
|
||||||
|
</q-btn>
|
||||||
<q-btn flat dense class="text-grey" icon="download" @click="downloadSVG">
|
<q-btn flat dense class="text-grey" icon="download" @click="downloadSVG">
|
||||||
<q-tooltip>Download SVG</q-tooltip>
|
<q-tooltip>Download SVG</q-tooltip>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
|
|||||||
+78
-32
@@ -5,7 +5,7 @@ from bolt11.decode import decode
|
|||||||
from grpc.aio import AioRpcError
|
from grpc.aio import AioRpcError
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
from lnbits.helpers import normalize_endpoint
|
from lnbits.helpers import normalize_endpoint, sha256s
|
||||||
from lnbits.settings import settings
|
from lnbits.settings import settings
|
||||||
from lnbits.wallets.boltz_grpc_files import boltzrpc_pb2, boltzrpc_pb2_grpc
|
from lnbits.wallets.boltz_grpc_files import boltzrpc_pb2, boltzrpc_pb2_grpc
|
||||||
from lnbits.wallets.lnd_grpc_files.lightning_pb2_grpc import grpc
|
from lnbits.wallets.lnd_grpc_files.lightning_pb2_grpc import grpc
|
||||||
@@ -38,10 +38,6 @@ class BoltzWallet(Wallet):
|
|||||||
raise ValueError(
|
raise ValueError(
|
||||||
"cannot initialize BoltzWallet: missing boltz_client_endpoint"
|
"cannot initialize BoltzWallet: missing boltz_client_endpoint"
|
||||||
)
|
)
|
||||||
if not settings.boltz_client_wallet:
|
|
||||||
raise ValueError(
|
|
||||||
"cannot initialize BoltzWallet: missing boltz_client_wallet"
|
|
||||||
)
|
|
||||||
|
|
||||||
self.endpoint = normalize_endpoint(
|
self.endpoint = normalize_endpoint(
|
||||||
settings.boltz_client_endpoint, add_proto=True
|
settings.boltz_client_endpoint, add_proto=True
|
||||||
@@ -63,31 +59,20 @@ class BoltzWallet(Wallet):
|
|||||||
|
|
||||||
self.rpc = boltzrpc_pb2_grpc.BoltzStub(channel)
|
self.rpc = boltzrpc_pb2_grpc.BoltzStub(channel)
|
||||||
self.wallet_id = 0
|
self.wallet_id = 0
|
||||||
|
self.wallet_name = "lnbits"
|
||||||
|
|
||||||
# Auto-create wallet if running in Docker mode
|
if settings.boltz_mnemonic: # restore wallet from mnemonic
|
||||||
async def _init_boltz_wallet():
|
self._init_wallet_task = asyncio.create_task(
|
||||||
try:
|
self._restore_boltz_wallet(
|
||||||
wallet_name = settings.boltz_client_wallet or "lnbits"
|
settings.boltz_mnemonic, settings.boltz_client_password
|
||||||
mnemonic = await self._fetch_wallet(wallet_name)
|
)
|
||||||
if mnemonic:
|
)
|
||||||
logger.info(
|
else: # create new wallet
|
||||||
"✅ Mnemonic found for Boltz wallet, saving to settings"
|
self._init_wallet_task = asyncio.create_task(self._create_boltz_wallet())
|
||||||
)
|
|
||||||
settings.boltz_mnemonic = mnemonic
|
|
||||||
|
|
||||||
from lnbits.core.crud.settings import set_settings_field
|
|
||||||
|
|
||||||
await set_settings_field("boltz_mnemonic", mnemonic)
|
|
||||||
else:
|
|
||||||
logger.warning("⚠️ No mnemonic returned from Boltz")
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"❌ Failed to auto-create Boltz wallet: {e}")
|
|
||||||
|
|
||||||
self._init_wallet_task = asyncio.create_task(_init_boltz_wallet())
|
|
||||||
|
|
||||||
async def status(self) -> StatusResponse:
|
async def status(self) -> StatusResponse:
|
||||||
try:
|
try:
|
||||||
request = boltzrpc_pb2.GetWalletRequest(name=settings.boltz_client_wallet)
|
request = boltzrpc_pb2.GetWalletRequest(name=self.wallet_name)
|
||||||
response: boltzrpc_pb2.Wallet = await self.rpc.GetWallet(
|
response: boltzrpc_pb2.Wallet = await self.rpc.GetWallet(
|
||||||
request, metadata=self.metadata
|
request, metadata=self.metadata
|
||||||
)
|
)
|
||||||
@@ -253,24 +238,85 @@ class BoltzWallet(Wallet):
|
|||||||
)
|
)
|
||||||
await asyncio.sleep(5)
|
await asyncio.sleep(5)
|
||||||
|
|
||||||
async def _fetch_wallet(self, wallet_name: str) -> str | None:
|
async def _check_wallet_exists(
|
||||||
|
self, wallet_name: str
|
||||||
|
) -> boltzrpc_pb2.Wallet | None:
|
||||||
try:
|
try:
|
||||||
request = boltzrpc_pb2.GetWalletRequest(name=wallet_name)
|
request = boltzrpc_pb2.GetWalletRequest(name=wallet_name)
|
||||||
response = await self.rpc.GetWallet(request, metadata=self.metadata)
|
response = await self.rpc.GetWallet(request, metadata=self.metadata)
|
||||||
logger.info(f"Wallet '{wallet_name}' already exists with ID {response.id}")
|
logger.info(f"Wallet '{wallet_name}' already exists with ID {response.id}")
|
||||||
return settings.boltz_mnemonic
|
return response
|
||||||
except AioRpcError as exc:
|
except AioRpcError as exc:
|
||||||
details = exc.details() or "unknown error"
|
|
||||||
if exc.code() != grpc.StatusCode.NOT_FOUND:
|
if exc.code() != grpc.StatusCode.NOT_FOUND:
|
||||||
logger.error(f"Error checking wallet existence: {details}")
|
logger.warning(f"Wallet '{wallet_name}' does not exist.")
|
||||||
raise
|
return None
|
||||||
|
logger.error(f"Error checking wallet existence: {exc.details()}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
async def _delete_wallet(self, wallet_id: int) -> None:
|
||||||
|
logger.info(f"Deleting wallet '{wallet_id}'")
|
||||||
|
delete_request = boltzrpc_pb2.RemoveWalletRequest(id=wallet_id)
|
||||||
|
await self.rpc.RemoveWallet(delete_request, metadata=self.metadata)
|
||||||
|
|
||||||
|
async def _restore_wallet(self, wallet_name: str, mnemonic: str, password: str):
|
||||||
|
logger.info(f"Restoring wallet '{wallet_name}' from mnemonic")
|
||||||
|
credentials = boltzrpc_pb2.WalletCredentials(mnemonic=mnemonic)
|
||||||
|
params = boltzrpc_pb2.WalletParams(
|
||||||
|
name=wallet_name,
|
||||||
|
currency=boltzrpc_pb2.LBTC,
|
||||||
|
password=password,
|
||||||
|
)
|
||||||
|
restore_request = boltzrpc_pb2.ImportWalletRequest(
|
||||||
|
credentials=credentials, params=params
|
||||||
|
)
|
||||||
|
response = await self.rpc.ImportWallet(restore_request, metadata=self.metadata)
|
||||||
|
return response
|
||||||
|
|
||||||
|
async def _create_wallet(self, wallet_name: str, password: str) -> str:
|
||||||
logger.info(f"Creating new wallet '{wallet_name}'")
|
logger.info(f"Creating new wallet '{wallet_name}'")
|
||||||
params = boltzrpc_pb2.WalletParams(
|
params = boltzrpc_pb2.WalletParams(
|
||||||
name=wallet_name,
|
name=wallet_name,
|
||||||
currency=boltzrpc_pb2.LBTC,
|
currency=boltzrpc_pb2.LBTC,
|
||||||
password=settings.boltz_client_password,
|
password=password,
|
||||||
)
|
)
|
||||||
create_request = boltzrpc_pb2.CreateWalletRequest(params=params)
|
create_request = boltzrpc_pb2.CreateWalletRequest(params=params)
|
||||||
response = await self.rpc.CreateWallet(create_request, metadata=self.metadata)
|
response = await self.rpc.CreateWallet(create_request, metadata=self.metadata)
|
||||||
return response.mnemonic
|
return response.mnemonic
|
||||||
|
|
||||||
|
async def _restore_boltz_wallet(self, mnemonic: str, password: str):
|
||||||
|
try:
|
||||||
|
# delete the wallet with to name without hashing first if it exists
|
||||||
|
wallet = await self._check_wallet_exists(self.wallet_name)
|
||||||
|
if wallet:
|
||||||
|
await self._delete_wallet(wallet.id)
|
||||||
|
|
||||||
|
# recreate the wallet with the hashed name
|
||||||
|
self.wallet_name = sha256s(mnemonic + password)
|
||||||
|
wallet = await self._check_wallet_exists(self.wallet_name)
|
||||||
|
if wallet:
|
||||||
|
logger.success("✅ Boltz wallet exists.")
|
||||||
|
return
|
||||||
|
await self._restore_wallet(self.wallet_name, mnemonic, password)
|
||||||
|
logger.success("✅ Boltz wallet restored from existing mnemonic")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"❌ Failed to restore Boltz wallet: {e}")
|
||||||
|
|
||||||
|
async def _create_boltz_wallet(self):
|
||||||
|
try:
|
||||||
|
# delete the wallet with to name without hashing first if it exists
|
||||||
|
wallet = await self._check_wallet_exists(self.wallet_name)
|
||||||
|
if wallet:
|
||||||
|
await self._delete_wallet(wallet.id)
|
||||||
|
|
||||||
|
logger.info("No Mnemonic found for Boltz wallet, creating wallet...")
|
||||||
|
mnemonic = await self._create_wallet(
|
||||||
|
self.wallet_name, settings.boltz_client_password
|
||||||
|
)
|
||||||
|
logger.success("✅ Boltz wallet created successfully")
|
||||||
|
settings.boltz_mnemonic = mnemonic
|
||||||
|
from lnbits.core.crud.settings import set_settings_field
|
||||||
|
|
||||||
|
await set_settings_field("boltz_mnemonic", mnemonic)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"❌ Failed to create Boltz wallet: {e}")
|
||||||
|
|||||||
Generated
+4
-9
@@ -1333,21 +1333,16 @@ pyjwt = ">=2.10.1,<3.0.0"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "filelock"
|
name = "filelock"
|
||||||
version = "3.18.0"
|
version = "3.20.1"
|
||||||
description = "A platform independent file lock."
|
description = "A platform independent file lock."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.9"
|
python-versions = ">=3.10"
|
||||||
groups = ["dev"]
|
groups = ["dev"]
|
||||||
files = [
|
files = [
|
||||||
{file = "filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de"},
|
{file = "filelock-3.20.1-py3-none-any.whl", hash = "sha256:15d9e9a67306188a44baa72f569d2bfd803076269365fdea0934385da4dc361a"},
|
||||||
{file = "filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2"},
|
{file = "filelock-3.20.1.tar.gz", hash = "sha256:b8360948b351b80f420878d8516519a2204b07aefcdcfd24912a5d33127f188c"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.extras]
|
|
||||||
docs = ["furo (>=2024.8.6)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"]
|
|
||||||
testing = ["covdefaults (>=2.3)", "coverage (>=7.6.10)", "diff-cover (>=9.2.1)", "pytest (>=8.3.4)", "pytest-asyncio (>=0.25.2)", "pytest-cov (>=6)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.28.1)"]
|
|
||||||
typing = ["typing-extensions (>=4.12.2) ; python_version < \"3.11\""]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "filetype"
|
name = "filetype"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "lnbits"
|
name = "lnbits"
|
||||||
version = "1.4.0-rc2"
|
version = "1.4.0-rc3"
|
||||||
requires-python = ">=3.10,<3.13"
|
requires-python = ">=3.10,<3.13"
|
||||||
description = "LNbits, free and open-source Lightning wallet and accounts system."
|
description = "LNbits, free and open-source Lightning wallet and accounts system."
|
||||||
authors = [{ name = "Alan Bits", email = "alan@lnbits.com" }]
|
authors = [{ name = "Alan Bits", email = "alan@lnbits.com" }]
|
||||||
|
|||||||
@@ -1247,7 +1247,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lnbits"
|
name = "lnbits"
|
||||||
version = "1.4.0rc2"
|
version = "1.4.0rc3"
|
||||||
source = { editable = "." }
|
source = { editable = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "aiosqlite" },
|
{ name = "aiosqlite" },
|
||||||
|
|||||||
Reference in New Issue
Block a user