CI: lnbits-boltz docker, for those who want instant payments + boltz funding source UX tweak (#3192)

Co-authored-by: jackstar12 <62219658+jackstar12@users.noreply.github.com>
Co-authored-by: Vlad Stan <stan.v.vlad@gmail.com>
Co-authored-by: dni  <office@dnilabs.com>
This commit is contained in:
Arc 2025-06-25 11:20:46 +01:00 committed by GitHub
parent 9c6efc4797
commit 2a7de2fa55
16 changed files with 1877 additions and 1707 deletions

View file

@ -101,8 +101,10 @@ ALBY_ACCESS_TOKEN=ALBY_ACCESS_TOKEN
# BoltzWallet
BOLTZ_CLIENT_ENDPOINT=127.0.0.1:9002
BOLTZ_CLIENT_MACAROON="/home/bob/.boltz/macaroon" # or HEXSTRING
BOLTZ_CLIENT_CERT="/home/bob/.boltz/tls.cert" # or HEXSTRING
# HEXSTRING instead of path also possible
BOLTZ_CLIENT_MACAROON="/home/bob/.boltz/macaroons/admin.macaroon"
# HEXSTRING instead of path also possible
BOLTZ_CLIENT_CERT="/home/bob/.boltz/tls.cert"
BOLTZ_CLIENT_WALLET="lnbits"
# StrikeWallet

View file

@ -51,3 +51,14 @@ jobs:
platforms: linux/amd64,linux/arm64
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache
- name: Build and push boltz
uses: docker/build-push-action@v5
with:
context: .
file: Dockerfile.boltz
push: true
tags: ${{ secrets.DOCKER_USERNAME }}/lnbits-boltz:${{ inputs.tag }}
platforms: linux/amd64,linux/arm64
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache

30
Dockerfile.boltz Normal file
View file

@ -0,0 +1,30 @@
FROM boltz/boltz-client:latest AS boltz
FROM lnbits/lnbits:latest
COPY --from=boltz /bin/boltzd /bin/boltzcli /usr/local/bin/
RUN ls -l /usr/local/bin/boltzd
RUN apt-get update && apt-get -y upgrade && \
apt-get install -y netcat-openbsd
# Reinstall dependencies just in case (needed for CMD usage)
ARG POETRY_INSTALL_ARGS="--only main"
RUN poetry install ${POETRY_INSTALL_ARGS}
# LNbits + boltzd configuration
ENV LNBITS_PORT="5000"
ENV LNBITS_HOST="0.0.0.0"
ENV LNBITS_BACKEND_WALLET_CLASS="BoltzWallet"
ENV FUNDING_SOURCE_MAX_RETRIES=10
ENV BOLTZ_CLIENT_ENDPOINT="127.0.0.1:9002"
ENV BOLTZ_CLIENT_MACAROON="/root/.boltz/macaroons/admin.macaroon"
ENV BOLTZ_CLIENT_CERT="/root/.boltz/tls.cert"
ENV BOLTZ_CLIENT_WALLET="lnbits"
EXPOSE 5000
# Entrypoint to start boltzd and LNbits
COPY dockerboltz.sh /dockerboltz.sh
RUN chmod +x /dockerboltz.sh
CMD ["/dockerboltz.sh"]

26
dockerboltz.sh Normal file
View file

@ -0,0 +1,26 @@
#!/bin/bash
set -e
boltzd --standalone --referralId lnbits &
# Capture boltzd PID to monitor if needed
BOLTZ_PID=$!
# Wait for boltzd to start
for i in {1..10}; do
if nc -z localhost 9002; then
echo "boltzd is up!"
break
fi
echo "Waiting for boltzd to start..."
sleep 1
done
# Optional: check if still not up
if ! nc -z localhost 9002; then
echo "boltzd did not start successfully."
exit 1
fi
echo "Starting LNbits on $LNBITS_HOST:$LNBITS_PORT..."
exec poetry run lnbits --port "$LNBITS_PORT" --host "$LNBITS_HOST" --forwarded-allow-ips='*'

View file

@ -553,7 +553,9 @@ class BoltzFundingSource(LNbitsSettings):
boltz_client_endpoint: str | None = Field(default="127.0.0.1:9002")
boltz_client_macaroon: str | None = Field(default=None)
boltz_client_wallet: str | None = Field(default="lnbits")
boltz_client_password: str = Field(default="")
boltz_client_cert: str | None = Field(default=None)
boltz_mnemonic: str | None = Field(default=None)
class StrikeFundingSource(LNbitsSettings):

File diff suppressed because one or more lines are too long

View file

@ -8,6 +8,10 @@ window.app.component('lnbits-funding-sources', {
fundingSource => fundingSource[0] === item
)
return fundingSource ? fundingSource[1] : item
},
showQRValue(value) {
this.qrValue = value
this.showQRDialog = true
}
},
computed: {
@ -17,7 +21,8 @@ window.app.component('lnbits-funding-sources', {
const tmpObj = {}
if (obj !== null) {
for (let [k, v] of Object.entries(obj)) {
tmpObj[k] = {label: v, value: null}
tmpObj[k] =
typeof v === 'string' ? {label: v, value: null} : v || {}
}
}
tmp.push([key, tmpObj])
@ -31,6 +36,8 @@ window.app.component('lnbits-funding-sources', {
data() {
return {
hideInput: true,
showQRDialog: false,
qrValue: '',
rawFundingSources: [
['VoidWallet', 'Void Wallet', null],
[
@ -138,7 +145,14 @@ window.app.component('lnbits-funding-sources', {
boltz_client_endpoint: 'Endpoint',
boltz_client_macaroon: 'Admin Macaroon path or hex',
boltz_client_cert: 'Certificate path or hex',
boltz_client_wallet: 'Wallet Name'
boltz_client_wallet: 'Wallet Name',
boltz_client_password: 'Wallet Password (can be empty)',
boltz_mnemonic: {
label: 'Liquid mnemonic (copy into greenwallet)',
readonly: true,
copy: true,
qrcode: true
}
}
],
[

View file

@ -1141,12 +1141,41 @@
:type="hideInput ? 'password' : 'text'"
:label="prop.label"
:hint="prop.hint"
:readonly="prop.readonly || false"
>
<q-btn
v-if="prop.copy"
@click="copyText(formData[key])"
icon="content_copy"
class="cursor-pointer"
color="grey"
flat
dense
></q-btn>
<q-btn
v-if="prop.qrcode"
@click="showQRValue(formData[key])"
icon="qr_code"
class="cursor-pointer"
color="grey"
flat
dense
></q-btn>
</q-input>
</div>
</div>
</div>
</q-list>
<q-dialog v-model="showQRDialog">
<q-card class="q-pa-md">
<q-card-section>
<lnbits-qrcode :value="qrValue" :size="200"></lnbits-qrcode>
</q-card-section>
<q-card-actions align="right">
<q-btn flat label="Close" v-close-popup />
</q-card-actions>
</q-card>
</q-dialog>
</div>
</template>

View file

@ -63,6 +63,27 @@ class BoltzWallet(Wallet):
self.rpc = boltzrpc_pb2_grpc.BoltzStub(channel)
self.wallet_id: int = 0
# Auto-create wallet if running in Docker mode
async def _init_boltz_wallet():
try:
wallet_name = settings.boltz_client_wallet or "lnbits"
mnemonic = await self._fetch_wallet(wallet_name)
if mnemonic:
logger.info(
"✅ Mnemonic found for Boltz wallet, saving to settings"
)
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:
try:
request = boltzrpc_pb2.GetWalletRequest(name=settings.boltz_client_wallet)
@ -70,9 +91,10 @@ class BoltzWallet(Wallet):
request, metadata=self.metadata
)
except AioRpcError as exc:
logger.warning(exc)
return StatusResponse(
exc.details()
+ " make sure you have macaroon and certificate configured, unless your client runs without", # noqa: E501
"make sure you have macaroon and certificate configured,"
"unless your client runs without",
0,
)
@ -193,7 +215,10 @@ class BoltzWallet(Wallet):
async def get_payment_status(self, checking_id: str) -> PaymentStatus:
try:
response: boltzrpc_pb2.GetSwapInfoResponse = await self.rpc.GetSwapInfo(
boltzrpc_pb2.GetSwapInfoRequest(id=checking_id), metadata=self.metadata
boltzrpc_pb2.GetSwapInfoRequest(
payment_hash=bytes.fromhex(checking_id)
),
metadata=self.metadata,
)
swap = response.swap
except AioRpcError:
@ -225,3 +250,25 @@ class BoltzWallet(Wallet):
" 5 seconds"
)
await asyncio.sleep(5)
async def _fetch_wallet(self, wallet_name: str) -> Optional[str]:
try:
request = boltzrpc_pb2.GetWalletRequest(name=wallet_name)
response = await self.rpc.GetWallet(request, metadata=self.metadata)
logger.info(f"Wallet '{wallet_name}' already exists with ID {response.id}")
return settings.boltz_mnemonic
except AioRpcError as exc:
details = exc.details() or "unknown error"
if exc.code() != grpc.StatusCode.NOT_FOUND:
logger.error(f"Error checking wallet existence: {details}")
raise
logger.info(f"Creating new wallet '{wallet_name}'")
params = boltzrpc_pb2.WalletParams(
name=wallet_name,
currency=boltzrpc_pb2.LBTC,
password=settings.boltz_client_password,
)
create_request = boltzrpc_pb2.CreateWalletRequest(params=params)
response = await self.rpc.CreateWallet(create_request, metadata=self.metadata)
return response.mnemonic

View file

@ -1,7 +1,7 @@
syntax = "proto3";
package boltzrpc;
option go_package = "github.com/BoltzExchange/boltz-client/boltzrpc";
option go_package = "github.com/BoltzExchange/boltz-client/v2/pkg/boltzrpc";
import "google/protobuf/empty.proto";
service Boltz {
@ -122,6 +122,23 @@ service Boltz {
*/
rpc GetWallet (GetWalletRequest) returns (Wallet);
/*
Calculates the fee for an equivalent `WalletSend` request.
If `address` is left empty, a dummy swap address will be used, allowing for a fee estimation of a swap lockup transaction.
*/
rpc GetWalletSendFee (WalletSendRequest) returns (WalletSendFee);
/*
Returns recent transactions from a wallet.
*/
rpc ListWalletTransactions (ListWalletTransactionsRequest) returns (ListWalletTransactionsResponse);
/*
Increase the fee of a transaction using RBF.
The transaction has to belong to one of the clients wallets.
*/
rpc BumpTransaction (BumpTransactionRequest) returns (BumpTransactionResponse);
/*
Returns the credentials of a wallet. The password will be required if the wallet is encrypted.
*/
@ -132,6 +149,16 @@ service Boltz {
*/
rpc RemoveWallet (RemoveWalletRequest) returns (RemoveWalletResponse);
/*
Send coins from a wallet. Only the confirmed balance can be spent.
*/
rpc WalletSend (WalletSendRequest) returns (WalletSendResponse);
/*
Get a new address of the wallet.
*/
rpc WalletReceive (WalletReceiveRequest) returns (WalletReceiveResponse);
/*
Gracefully stops the daemon.
*/
@ -265,11 +292,12 @@ message SwapInfo {
repeated ChannelId chan_ids = 14;
optional string blinding_key = 15;
int64 created_at = 16;
optional uint64 service_fee = 17;
optional int64 service_fee = 17;
optional uint64 onchain_fee = 18;
// internal wallet which was used to pay the swap
optional uint64 wallet_id = 20;
uint64 tenant_id = 21;
bool is_auto = 23;
}
enum SwapType {
@ -322,7 +350,8 @@ message ReverseSwapInfo {
string redeem_script = 7;
string invoice = 8;
string claim_address = 9;
int64 onchain_amount = 10;
uint64 onchain_amount = 10;
uint64 invoice_amount = 25;
uint32 timeout_block_height = 11;
string lockup_transaction_id = 12;
string claim_transaction_id = 13;
@ -331,11 +360,12 @@ message ReverseSwapInfo {
optional string blinding_key = 16;
int64 created_at = 17;
optional int64 paid_at = 23; // the time when the invoice was paid
optional uint64 service_fee = 18;
optional int64 service_fee = 18;
optional uint64 onchain_fee = 19;
optional uint64 routing_fee_msat = 20;
bool external_pay = 21;
uint64 tenant_id = 22;
bool is_auto = 24;
}
message BlockHeights {
@ -406,17 +436,44 @@ enum IncludeSwaps {
AUTO = 2;
}
message AnySwapInfo {
string id = 1;
SwapType type = 2;
Pair pair = 3;
SwapState state = 4;
optional string error = 5;
string status = 6;
// The expected amount to be sent to the lockup address for submarine and chain swaps and
// the invoice amount for reverse swaps.
uint64 from_amount = 7;
// `from_amount` minus the service and network fee.
uint64 to_amount = 13;
int64 created_at = 8;
optional int64 service_fee = 9;
// inclues the routing fee for reverse swaps
optional uint64 onchain_fee = 10;
bool is_auto = 11;
uint64 tenant_id = 12;
}
message ListSwapsRequest {
optional Currency from = 1;
optional Currency to = 2;
optional SwapState state = 4;
IncludeSwaps include = 5;
optional uint64 limit = 6;
optional uint64 offset = 7;
// wether to return swaps in the shared `all_swaps` list or in the detailed lists.
// the `limit` and `offset` are only considered when `unify` is true.
optional bool unify = 8;
}
message ListSwapsResponse {
repeated SwapInfo swaps = 1;
repeated CombinedChannelSwapInfo channel_creations = 2;
repeated CombinedChannelSwapInfo channel_creations = 2 [deprecated = true];
repeated ReverseSwapInfo reverse_swaps = 3;
repeated ChainSwapInfo chain_swaps = 4;
// populated when `unify` is set to true in the request
repeated AnySwapInfo all_swaps = 5;
}
message GetStatsRequest {
@ -448,7 +505,12 @@ message ClaimSwapsResponse {
}
message GetSwapInfoRequest {
string id = 1;
string id = 1 [deprecated = true];
oneof identifier {
string swap_id = 2;
// Only implemented for submarine swaps
bytes payment_hash = 3;
}
}
message GetSwapInfoResponse {
SwapInfo swap = 1;
@ -471,22 +533,29 @@ message DepositResponse {
}
message CreateSwapRequest {
// amount of sats to be received on lightning.
// related: `invoice` field
uint64 amount = 1;
Pair pair = 2;
// not yet supported
// repeated string chan_ids = 3;
// the daemon will pay the swap using the onchain wallet specified in the `wallet` field or any wallet otherwise.
// the daemon will pay the swap using the onchain wallet specified in the `wallet` field
// or the first internal wallet with the correct currency otherwise.
bool send_from_internal = 4;
// address where the coins should go if the swap fails. Refunds will go to any of the daemons wallets otherwise.
optional string refund_address = 5;
// wallet to pay swap from. only used if `send_from_internal` is set to true
optional uint64 wallet_id = 6;
// invoice to use for the swap. if not set, the daemon will get a new invoice from the lightning node
// bolt11 invoice, lnurl, or lnaddress to use for the swap.
// required in standalone mode.
// when connected to a lightning node, a new invoice for `amount` sats will be fetched
// the `amount` field has to be populated in case of a lnurl and lnaddress
optional string invoice = 7;
// Boltz does not accept 0-conf for Liquid transactions with a fee of 0.01 sat/vByte;
// when `zero_conf` is enabled, a fee of 0.1 sat/vByte will be used for Liquid lockup transactions
optional bool zero_conf = 8;
optional bool zero_conf = 8 [deprecated = true];
// Fee rate to use when sending from internal wallet
optional double sat_per_vbyte = 9;
// Rates to accept for the swap. Queries latest from boltz otherwise
// The recommended way to use this is to pass a user approved value from a previous `GetPairInfo` call
optional PairInfo accepted_pair = 10;
}
message CreateSwapResponse {
string id = 1;
@ -530,6 +599,16 @@ message CreateReverseSwapRequest {
optional bool external_pay = 8;
// Description of the invoice which will be created for the swap
optional string description = 9;
// Description hash of the invoice which will be created for the swap. Takes precedence over `description`
optional bytes description_hash = 10;
// Expiry of the reverse swap invoice in seconds
optional uint64 invoice_expiry = 11;
// Rates to accept for the swap. Queries latest from boltz otherwise
// The recommended way to use this is to pass a user approved value from a previous `GetPairInfo` call
optional PairInfo accepted_pair = 12;
// The routing fee limit for paying the lightning invoice in ppm (parts per million)
optional uint64 routing_fee_limit_ppm = 13;
}
message CreateReverseSwapResponse {
string id = 1;
@ -545,7 +624,8 @@ message CreateReverseSwapResponse {
message CreateChainSwapRequest {
// Amount of satoshis to swap. It is the amount expected to be sent to the lockup address.
uint64 amount = 1;
// If left empty, any amount within the limits will be accepted.
optional uint64 amount = 1;
Pair pair = 2;
// Address where funds will be swept to if the swap succeeds
optional string to_address = 3;
@ -561,9 +641,12 @@ message CreateChainSwapRequest {
optional bool accept_zero_conf = 7;
// If set, the daemon will not pay the swap from an internal wallet.
optional bool external_pay = 8;
// Boltz does not accept 0-conf for Liquid transactions with a fee of 0.01 sat/vByte;
// when `lockup_zero_conf` is enabled, a fee of 0.1 sat/vByte will be used for Liquid lockup transactions
optional bool lockup_zero_conf = 9;
optional bool lockup_zero_conf = 9 [deprecated = true];
// Fee rate to use when sending from internal wallet
optional double sat_per_vbyte = 10;
// Rates to accept for the swap. Queries latest from boltz otherwise
// The recommended way to use this is to pass a user approved value from a previous `GetPairInfo` call
optional PairInfo accepted_pair = 11;
}
message ChainSwapInfo {
@ -574,7 +657,7 @@ message ChainSwapInfo {
string status = 5;
string preimage = 6;
bool is_auto = 8;
optional uint64 service_fee = 9;
optional int64 service_fee = 9;
double service_fee_percent = 10;
optional uint64 onchain_fee = 11;
int64 created_at = 12;
@ -614,9 +697,9 @@ message LightningChannel {
}
message SwapStats {
uint64 total_fees = 1;
int64 total_fees = 1;
uint64 total_amount = 2;
uint64 avg_fees = 3;
int64 avg_fees = 3;
uint64 avg_amount = 4;
uint64 count = 5;
uint64 success_count = 6;
@ -687,6 +770,75 @@ message GetWalletRequest {
optional uint64 id = 2;
}
message WalletSendFee {
// amount of sats which would be sent
uint64 amount = 1;
uint64 fee = 2;
// the fee rate used for the estimation in sat/vbyte
double fee_rate = 3;
}
message ListWalletTransactionsRequest {
uint64 id = 1;
optional bool exclude_swap_related = 2;
optional uint64 limit = 3;
optional uint64 offset = 4;
}
enum TransactionType {
UNKNOWN = 0;
LOCKUP = 1;
REFUND = 2;
CLAIM = 3;
CONSOLIDATION = 4;
}
message WalletTransaction {
string id = 1;
// balance change of the wallet in satoshis.
// its the sum of all output values minus the sum of all input values which are controlled by the wallet.
// positive values indicate incoming transactions, negative values outgoing transactions
int64 balance_change = 2;
int64 timestamp = 3;
repeated TransactionOutput outputs = 4;
uint32 block_height = 6;
// additional informations about the tx (type, related swaps etc.)
repeated TransactionInfo infos = 7;
}
message BumpTransactionRequest {
oneof previous {
// Id of the transaction to bump. The transaction has to belong to one of the clients wallets
string tx_id = 1;
// Depending on the state of the swap, the lockup, refund or claim transaction will be bumped
string swap_id = 2;
}
// Fee rate for the new transaction. if not specified, the daemon will query the fee rate from the configured provider
// and bump the fee by at least 1 sat/vbyte.
optional double sat_per_vbyte = 3;
}
message BumpTransactionResponse {
string tx_id = 1;
}
message TransactionInfo {
// will be populated for LOCKUP, REFUND and CLAIM
optional string swap_id = 1;
TransactionType type = 2;
}
message TransactionOutput {
string address = 1;
uint64 amount = 2;
// wether the address is controlled by the wallet
bool is_our_address = 3;
}
message ListWalletTransactionsResponse {
repeated WalletTransaction transactions = 1;
}
message GetWalletCredentialsRequest {
uint64 id = 1;
optional string password = 2;
@ -696,6 +848,31 @@ message RemoveWalletRequest {
uint64 id = 1;
}
message WalletSendRequest {
uint64 id = 1;
string address = 2;
// Amount of satoshis to be sent to 'address`
uint64 amount = 3;
// Fee rate to use for the transaction
optional double sat_per_vbyte = 4;
// Sends all available funds to the address. The `amount` field is ignored.
optional bool send_all = 5;
// whether `address` is the lockup of a swap.
optional bool is_swap_address = 6;
}
message WalletSendResponse {
string tx_id = 1;
}
message WalletReceiveRequest {
uint64 id = 1;
}
message WalletReceiveResponse {
string address = 1;
}
message Wallet {
uint64 id = 1;
@ -720,6 +897,7 @@ message Subaccount {
Balance balance = 1;
uint64 pointer = 2;
string type = 3;
repeated string descriptors = 4;
}
message RemoveWalletResponse {}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,3 +1,3 @@
wget https://raw.githubusercontent.com/BoltzExchange/boltz-client/master/boltzrpc/boltzrpc.proto -O lnbits/wallets/boltz_grpc_files/boltzrpc.proto
poetry run python -m grpc_tools.protoc -I . --python_out=. --grpc_python_out=. --pyi_out=. lnbits/wallets/boltz_grpc_files/boltzrpc.proto
wget https://raw.githubusercontent.com/BoltzExchange/boltz-client/refs/heads/master/pkg/boltzrpc/boltzrpc.proto -O boltz_grpc_files/boltzrpc.proto
poetry run python -m grpc_tools.protoc -I . --python_out=. --grpc_python_out=. --pyi_out=. boltz_grpc_files/boltzrpc.proto

26
poetry.lock generated
View file

@ -2216,23 +2216,23 @@ virtualenv = ">=20.10.0"
[[package]]
name = "protobuf"
version = "5.28.0"
version = "5.29.1"
description = ""
optional = false
python-versions = ">=3.8"
groups = ["main", "dev"]
files = [
{file = "protobuf-5.28.0-cp310-abi3-win32.whl", hash = "sha256:66c3edeedb774a3508ae70d87b3a19786445fe9a068dd3585e0cefa8a77b83d0"},
{file = "protobuf-5.28.0-cp310-abi3-win_amd64.whl", hash = "sha256:6d7cc9e60f976cf3e873acb9a40fed04afb5d224608ed5c1a105db4a3f09c5b6"},
{file = "protobuf-5.28.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:532627e8fdd825cf8767a2d2b94d77e874d5ddb0adefb04b237f7cc296748681"},
{file = "protobuf-5.28.0-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:018db9056b9d75eb93d12a9d35120f97a84d9a919bcab11ed56ad2d399d6e8dd"},
{file = "protobuf-5.28.0-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:6206afcb2d90181ae8722798dcb56dc76675ab67458ac24c0dd7d75d632ac9bd"},
{file = "protobuf-5.28.0-cp38-cp38-win32.whl", hash = "sha256:eef7a8a2f4318e2cb2dee8666d26e58eaf437c14788f3a2911d0c3da40405ae8"},
{file = "protobuf-5.28.0-cp38-cp38-win_amd64.whl", hash = "sha256:d001a73c8bc2bf5b5c1360d59dd7573744e163b3607fa92788b7f3d5fefbd9a5"},
{file = "protobuf-5.28.0-cp39-cp39-win32.whl", hash = "sha256:dde9fcaa24e7a9654f4baf2a55250b13a5ea701493d904c54069776b99a8216b"},
{file = "protobuf-5.28.0-cp39-cp39-win_amd64.whl", hash = "sha256:853db610214e77ee817ecf0514e0d1d052dff7f63a0c157aa6eabae98db8a8de"},
{file = "protobuf-5.28.0-py3-none-any.whl", hash = "sha256:510ed78cd0980f6d3218099e874714cdf0d8a95582e7b059b06cabad855ed0a0"},
{file = "protobuf-5.28.0.tar.gz", hash = "sha256:dde74af0fa774fa98892209992295adbfb91da3fa98c8f67a88afe8f5a349add"},
{file = "protobuf-5.29.1-cp310-abi3-win32.whl", hash = "sha256:22c1f539024241ee545cbcb00ee160ad1877975690b16656ff87dde107b5f110"},
{file = "protobuf-5.29.1-cp310-abi3-win_amd64.whl", hash = "sha256:1fc55267f086dd4050d18ef839d7bd69300d0d08c2a53ca7df3920cc271a3c34"},
{file = "protobuf-5.29.1-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:d473655e29c0c4bbf8b69e9a8fb54645bc289dead6d753b952e7aa660254ae18"},
{file = "protobuf-5.29.1-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:b5ba1d0e4c8a40ae0496d0e2ecfdbb82e1776928a205106d14ad6985a09ec155"},
{file = "protobuf-5.29.1-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:8ee1461b3af56145aca2800e6a3e2f928108c749ba8feccc6f5dd0062c410c0d"},
{file = "protobuf-5.29.1-cp38-cp38-win32.whl", hash = "sha256:50879eb0eb1246e3a5eabbbe566b44b10348939b7cc1b267567e8c3d07213853"},
{file = "protobuf-5.29.1-cp38-cp38-win_amd64.whl", hash = "sha256:027fbcc48cea65a6b17028510fdd054147057fa78f4772eb547b9274e5219331"},
{file = "protobuf-5.29.1-cp39-cp39-win32.whl", hash = "sha256:5a41deccfa5e745cef5c65a560c76ec0ed8e70908a67cc8f4da5fce588b50d57"},
{file = "protobuf-5.29.1-cp39-cp39-win_amd64.whl", hash = "sha256:012ce28d862ff417fd629285aca5d9772807f15ceb1a0dbd15b88f58c776c98c"},
{file = "protobuf-5.29.1-py3-none-any.whl", hash = "sha256:32600ddb9c2a53dedc25b8581ea0f1fd8ea04956373c0c07577ce58d312522e0"},
{file = "protobuf-5.29.1.tar.gz", hash = "sha256:683be02ca21a6ffe80db6dd02c0b5b2892322c59ca57fd6c872d652cb80549cb"},
]
[[package]]
@ -3747,4 +3747,4 @@ liquid = ["wallycore"]
[metadata]
lock-version = "2.1"
python-versions = "~3.12 | ~3.11 | ~3.10"
content-hash = "9259359a900bdf58e224f8e0e8cedd54dfb256a126baa0e3d0262a15ccc4d768"
content-hash = "8a345c03b3103cce8b278612308d7aa787dda35fc1bfece6d22cc858f618f527"

View file

@ -33,7 +33,7 @@ uvloop = "0.21.0"
websockets = "11.0.3"
loguru = "0.7.2"
grpcio = "1.69.0"
protobuf = "5.28.0"
protobuf = "5.29.1"
pyln-client = "24.11"
pywebpush = "1.14.1"
slowapi = "0.1.9"