mirror of
https://github.com/lnbits/lnbits-legend.git
synced 2025-02-24 22:58:46 +01:00
246 lines
7 KiB
Python
246 lines
7 KiB
Python
import json
|
|
from typing import List, Optional
|
|
|
|
from lnbits.helpers import urlsafe_short_hash
|
|
|
|
from . import db
|
|
from .helpers import derive_address
|
|
from .models import Address, Config, WalletAccount
|
|
|
|
##########################WALLETS####################
|
|
|
|
|
|
async def create_watch_wallet(user: str, w: WalletAccount) -> WalletAccount:
|
|
wallet_id = urlsafe_short_hash()
|
|
await db.execute(
|
|
"""
|
|
INSERT INTO watchonly.wallets (
|
|
id,
|
|
"user",
|
|
masterpub,
|
|
fingerprint,
|
|
title,
|
|
type,
|
|
address_no,
|
|
balance,
|
|
network,
|
|
meta
|
|
)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
""",
|
|
(
|
|
wallet_id,
|
|
user,
|
|
w.masterpub,
|
|
w.fingerprint,
|
|
w.title,
|
|
w.type,
|
|
w.address_no,
|
|
w.balance,
|
|
w.network,
|
|
w.meta,
|
|
),
|
|
)
|
|
wallet = await get_watch_wallet(wallet_id)
|
|
assert wallet
|
|
return wallet
|
|
|
|
|
|
async def get_watch_wallet(wallet_id: str) -> Optional[WalletAccount]:
|
|
row = await db.fetchone(
|
|
"SELECT * FROM watchonly.wallets WHERE id = ?", (wallet_id,)
|
|
)
|
|
return WalletAccount.from_row(row) if row else None
|
|
|
|
|
|
async def get_watch_wallets(user: str, network: str) -> List[WalletAccount]:
|
|
rows = await db.fetchall(
|
|
"""SELECT * FROM watchonly.wallets WHERE "user" = ? AND network = ?""",
|
|
(user, network),
|
|
)
|
|
return [WalletAccount(**row) for row in rows]
|
|
|
|
|
|
async def update_watch_wallet(wallet_id: str, **kwargs) -> Optional[WalletAccount]:
|
|
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
|
|
|
|
await db.execute(
|
|
f"UPDATE watchonly.wallets SET {q} WHERE id = ?", (*kwargs.values(), wallet_id)
|
|
)
|
|
row = await db.fetchone(
|
|
"SELECT * FROM watchonly.wallets WHERE id = ?", (wallet_id,)
|
|
)
|
|
return WalletAccount.from_row(row) if row else None
|
|
|
|
|
|
async def delete_watch_wallet(wallet_id: str) -> None:
|
|
await db.execute("DELETE FROM watchonly.wallets WHERE id = ?", (wallet_id,))
|
|
|
|
|
|
########################ADDRESSES#######################
|
|
|
|
|
|
async def get_fresh_address(wallet_id: str) -> Optional[Address]:
|
|
# todo: move logic to views_api after satspay refactoring
|
|
wallet = await get_watch_wallet(wallet_id)
|
|
|
|
if not wallet:
|
|
return None
|
|
|
|
wallet_addresses = await get_addresses(wallet_id)
|
|
receive_addresses = list(
|
|
filter(
|
|
lambda addr: addr.branch_index == 0 and addr.has_activity, wallet_addresses
|
|
)
|
|
)
|
|
last_receive_index = (
|
|
receive_addresses.pop().address_index if receive_addresses else -1
|
|
)
|
|
address_index = (
|
|
last_receive_index
|
|
if last_receive_index > wallet.address_no
|
|
else wallet.address_no
|
|
)
|
|
|
|
address = await get_address_at_index(wallet_id, 0, address_index + 1)
|
|
|
|
if not address:
|
|
addresses = await create_fresh_addresses(
|
|
wallet_id, address_index + 1, address_index + 2
|
|
)
|
|
address = addresses.pop()
|
|
|
|
await update_watch_wallet(wallet_id, **{"address_no": address_index + 1})
|
|
|
|
return address
|
|
|
|
|
|
async def create_fresh_addresses(
|
|
wallet_id: str,
|
|
start_address_index: int,
|
|
end_address_index: int,
|
|
change_address=False,
|
|
) -> List[Address]:
|
|
if start_address_index > end_address_index:
|
|
return []
|
|
|
|
wallet = await get_watch_wallet(wallet_id)
|
|
if not wallet:
|
|
return []
|
|
|
|
branch_index = 1 if change_address else 0
|
|
|
|
for address_index in range(start_address_index, end_address_index):
|
|
address = await derive_address(wallet.masterpub, address_index, branch_index)
|
|
|
|
await db.execute(
|
|
"""
|
|
INSERT INTO watchonly.addresses (
|
|
id,
|
|
address,
|
|
wallet,
|
|
amount,
|
|
branch_index,
|
|
address_index
|
|
)
|
|
VALUES (?, ?, ?, ?, ?, ?)
|
|
""",
|
|
(urlsafe_short_hash(), address, wallet_id, 0, branch_index, address_index),
|
|
)
|
|
|
|
# return fresh addresses
|
|
rows = await db.fetchall(
|
|
"""
|
|
SELECT * FROM watchonly.addresses
|
|
WHERE wallet = ? AND branch_index = ? AND address_index >= ? AND address_index < ?
|
|
ORDER BY branch_index, address_index
|
|
""",
|
|
(wallet_id, branch_index, start_address_index, end_address_index),
|
|
)
|
|
|
|
return [Address(**row) for row in rows]
|
|
|
|
|
|
async def get_address(address: str) -> Optional[Address]:
|
|
row = await db.fetchone(
|
|
"SELECT * FROM watchonly.addresses WHERE address = ?", (address,)
|
|
)
|
|
return Address.from_row(row) if row else None
|
|
|
|
|
|
async def get_address_at_index(
|
|
wallet_id: str, branch_index: int, address_index: int
|
|
) -> Optional[Address]:
|
|
row = await db.fetchone(
|
|
"""
|
|
SELECT * FROM watchonly.addresses
|
|
WHERE wallet = ? AND branch_index = ? AND address_index = ?
|
|
""",
|
|
(
|
|
wallet_id,
|
|
branch_index,
|
|
address_index,
|
|
),
|
|
)
|
|
return Address.from_row(row) if row else None
|
|
|
|
|
|
async def get_addresses(wallet_id: str) -> List[Address]:
|
|
rows = await db.fetchall(
|
|
"""
|
|
SELECT * FROM watchonly.addresses WHERE wallet = ?
|
|
ORDER BY branch_index, address_index
|
|
""",
|
|
(wallet_id,),
|
|
)
|
|
|
|
return [Address(**row) for row in rows]
|
|
|
|
|
|
async def update_address(id: str, **kwargs) -> Optional[Address]:
|
|
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
|
|
|
|
await db.execute(
|
|
f"""UPDATE watchonly.addresses SET {q} WHERE id = ? """,
|
|
(*kwargs.values(), id),
|
|
)
|
|
row = await db.fetchone("SELECT * FROM watchonly.addresses WHERE id = ?", (id,))
|
|
return Address.from_row(row) if row else None
|
|
|
|
|
|
async def delete_addresses_for_wallet(wallet_id: str) -> None:
|
|
await db.execute("DELETE FROM watchonly.addresses WHERE wallet = ?", (wallet_id,))
|
|
|
|
|
|
######################CONFIG#######################
|
|
async def create_config(user: str) -> Config:
|
|
config = Config()
|
|
await db.execute(
|
|
"""
|
|
INSERT INTO watchonly.config ("user", json_data)
|
|
VALUES (?, ?)
|
|
""",
|
|
(user, json.dumps(config.dict())),
|
|
)
|
|
row = await db.fetchone(
|
|
"""SELECT json_data FROM watchonly.config WHERE "user" = ?""", (user,)
|
|
)
|
|
return json.loads(row[0], object_hook=lambda d: Config(**d))
|
|
|
|
|
|
async def update_config(config: Config, user: str) -> Optional[Config]:
|
|
await db.execute(
|
|
f"""UPDATE watchonly.config SET json_data = ? WHERE "user" = ?""",
|
|
(json.dumps(config.dict()), user),
|
|
)
|
|
row = await db.fetchone(
|
|
"""SELECT json_data FROM watchonly.config WHERE "user" = ?""", (user,)
|
|
)
|
|
return json.loads(row[0], object_hook=lambda d: Config(**d))
|
|
|
|
|
|
async def get_config(user: str) -> Optional[Config]:
|
|
row = await db.fetchone(
|
|
"""SELECT json_data FROM watchonly.config WHERE "user" = ?""", (user,)
|
|
)
|
|
return json.loads(row[0], object_hook=lambda d: Config(**d)) if row else None
|