Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 33 additions & 25 deletions boltz_client/boltz.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,36 +114,44 @@ def __init__(self, config: BoltzConfig, pair: str = "BTC/BTC"):
f"invalid pair {pair}, possible pairs: {', '.join(self._cfg.pairs)}"
)
self.pair = pair
self.pairs = self.get_pairs()
self.fees = self.pairs[self.pair]["fees"]
self.limits = self.pairs[self.pair]["limits"]

if self.pair == "L-BTC/BTC":
self.network = self._cfg.network_liquid
else:
self.network = self._cfg.network
return None

def request(self, funcname, *args, **kwargs) -> dict:
async def init_pairs(self):
self.pairs = await self.get_pairs()
self.fees = self.pairs[self.pair]["fees"]
self.limits = self.pairs[self.pair]["limits"]

async def request(self, funcname, *args, **kwargs) -> dict:
try:
return req_wrap(funcname, *args, **kwargs)
return await req_wrap(funcname, *args, **kwargs)
except httpx.RequestError as exc:
msg = f"unreachable: {exc.request.url!r}."
raise BoltzApiException(f"boltz api connection error: {msg}") from exc
except httpx.HTTPStatusError as exc:
if exc.response.status_code == 404:
raise BoltzNotFoundException(exc.response.json()["error"]) from exc
msg = f"{exc.response.status_code} while requesting {exc.request.url!r}. message: {exc.response.json()['error']}"
try:
err_msg = exc.response.json()["error"]
except Exception:
err_msg = str(exc)
code = exc.response.status_code
if code == 404:
raise BoltzNotFoundException(err_msg) from exc
msg = f"{code} while requesting {exc.request.url!r}. message: {err_msg}"
raise BoltzApiException(f"boltz api status error: {msg}") from exc

def check_version(self):
return self.request(
async def check_version(self):
return await self.request(
"get",
f"{self._cfg.api_url}/version",
headers={"Content-Type": "application/json"},
)

def send_onchain_tx(self, rawtw: str) -> str:
data = self.request(
async def send_onchain_tx(self, rawtw: str) -> str:
data = await self.request(
"post",
f"{self._cfg.api_url}/broadcasttransaction",
headers={"Content-Type": "application/json"},
Expand All @@ -168,8 +176,8 @@ def get_fee_estimation_claim(self) -> int:
def get_fee_estimation_refund(self) -> int:
return self.fees["minerFees"]["baseAsset"]["normal"]

def get_pairs(self) -> dict:
data = self.request(
async def get_pairs(self) -> dict:
data = await self.request(
"get",
f"{self._cfg.api_url}/getpairs",
headers={"Content-Type": "application/json"},
Expand All @@ -185,8 +193,8 @@ def check_limits(self, amount: int) -> None:
f"min: {limits['minimal']}, max: {limits['maximal']}"
)

def swap_status(self, boltz_id: str) -> BoltzSwapStatusResponse:
data = self.request(
async def swap_status(self, boltz_id: str) -> BoltzSwapStatusResponse:
data = await self.request(
"post",
f"{self._cfg.api_url}/swapstatus",
json={"id": boltz_id},
Expand All @@ -199,8 +207,8 @@ def swap_status(self, boltz_id: str) -> BoltzSwapStatusResponse:

return status

def swap_transaction(self, boltz_id: str) -> BoltzSwapTransactionResponse:
data = self.request(
async def swap_transaction(self, boltz_id: str) -> BoltzSwapTransactionResponse:
data = await self.request(
"post",
f"{self._cfg.api_url}/getswaptransaction",
json={"id": boltz_id},
Expand All @@ -216,7 +224,7 @@ def swap_transaction(self, boltz_id: str) -> BoltzSwapTransactionResponse:
async def wait_for_tx(self, boltz_id: str) -> str:
while True:
try:
swap_transaction = self.swap_transaction(boltz_id)
swap_transaction = await self.swap_transaction(boltz_id)
assert swap_transaction.transactionHex
return swap_transaction.transactionHex
except (ValueError, BoltzApiException, BoltzSwapTransactionException):
Expand All @@ -225,7 +233,7 @@ async def wait_for_tx(self, boltz_id: str) -> str:
async def wait_for_tx_on_status(self, boltz_id: str, zeroconf: bool = True) -> str:
while True:
try:
status = self.swap_status(boltz_id)
status = await self.swap_status(boltz_id)
assert status.transaction
tx_hex = status.transaction.get("hex")
assert tx_hex
Expand Down Expand Up @@ -295,12 +303,12 @@ async def refund_swap(
blinding_key=blinding_key,
fees=self.get_fee_estimation_refund(),
)
return self.send_onchain_tx(transaction)
return await self.send_onchain_tx(transaction)

def create_swap(self, payment_request: str) -> tuple[str, BoltzSwapResponse]:
async def create_swap(self, payment_request: str) -> tuple[str, BoltzSwapResponse]:
"""create swap and return private key and boltz response"""
refund_privkey_wif, refund_pubkey_hex = create_key_pair(self.network, self.pair)
data = self.request(
data = await self.request(
"post",
f"{self._cfg.api_url}/createswap",
json={
Expand All @@ -315,14 +323,14 @@ def create_swap(self, payment_request: str) -> tuple[str, BoltzSwapResponse]:
)
return refund_privkey_wif, BoltzSwapResponse(**data)

def create_reverse_swap(
async def create_reverse_swap(
self, amount: int = 0
) -> tuple[str, str, BoltzReverseSwapResponse]:
"""create reverse swap and return privkey, preimage and boltz response"""
self.check_limits(amount)
claim_privkey_wif, claim_pubkey_hex = create_key_pair(self.network, self.pair)
preimage_hex, preimage_hash = create_preimage()
data = self.request(
data = await self.request(
"post",
f"{self._cfg.api_url}/createswap",
json={
Expand Down
27 changes: 17 additions & 10 deletions boltz_client/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def command_group():
@click.command()
@click.argument("payment_request", type=str)
@click.argument("pair", type=str, default="BTC/BTC")
def create_swap(payment_request: str, pair: str = "BTC/BTC"):
async def create_swap(payment_request: str, pair: str = "BTC/BTC"):
"""
create a swap
boltz will pay your invoice after you paid the onchain address
Expand All @@ -43,7 +43,7 @@ def create_swap(payment_request: str, pair: str = "BTC/BTC"):
PAYMENT_REQUEST with the same amount as specified in SATS
"""
client = BoltzClient(config, pair)
refund_privkey_wif, swap = client.create_swap(payment_request)
refund_privkey_wif, swap = await client.create_swap(payment_request)

click.echo()
click.echo(f"boltz_id: {swap.id}")
Expand Down Expand Up @@ -109,7 +109,9 @@ def refund_swap(
@click.argument("sats", type=int)
@click.argument("pair", type=str, default="BTC/BTC")
@click.argument("direction", type=str, default="send")
def create_reverse_swap(sats: int, pair: str = "BTC/BTC", direction: str = "send"):
async def create_reverse_swap(
sats: int, pair: str = "BTC/BTC", direction: str = "send"
):
"""
create a reverse swap
"""
Expand All @@ -123,7 +125,7 @@ def create_reverse_swap(sats: int, pair: str = "BTC/BTC", direction: str = "send
raise ValueError(
f"direction must be '{SwapDirection.send}' or '{SwapDirection.receive}'"
)
claim_privkey_wif, preimage_hex, swap = client.create_reverse_swap(sats)
claim_privkey_wif, preimage_hex, swap = await client.create_reverse_swap(sats)

click.echo("reverse swap created!")
click.echo()
Expand Down Expand Up @@ -155,7 +157,7 @@ def create_reverse_swap(sats: int, pair: str = "BTC/BTC", direction: str = "send
@click.argument("pair", type=str, default="BTC/BTC")
@click.argument("zeroconf", type=bool, default=True)
@click.argument("direction", type=str, default="send")
def create_reverse_swap_and_claim(
async def create_reverse_swap_and_claim(
receive_address: str,
sats: int,
pair: str = "BTC/BTC",
Expand All @@ -166,6 +168,7 @@ def create_reverse_swap_and_claim(
create a reverse swap and claim
"""
client = BoltzClient(config, pair)
await client.init_pairs()
if direction == SwapDirection.receive:
sats = client.add_reverse_swap_fees(sats)
elif direction == SwapDirection.send:
Expand All @@ -176,7 +179,7 @@ def create_reverse_swap_and_claim(
f"direction must be '{SwapDirection.send}' or '{SwapDirection.receive}'"
)

claim_privkey_wif, preimage_hex, swap = client.create_reverse_swap(sats)
claim_privkey_wif, preimage_hex, swap = await client.create_reverse_swap(sats)

click.echo("reverse swap created!")
click.echo()
Expand Down Expand Up @@ -224,7 +227,7 @@ def create_reverse_swap_and_claim(
@click.argument("pair", type=str, default="BTC/BTC")
@click.argument("zeroconf", type=bool, default=True)
@click.argument("blinding_key", type=str, default=None)
def claim_reverse_swap(
async def claim_reverse_swap(
boltz_id: str,
lockup_address: str,
receive_address: str,
Expand All @@ -239,6 +242,7 @@ def claim_reverse_swap(
claims a reverse swap
"""
client = BoltzClient(config, pair)
await client.init_pairs()

txid = asyncio.run(
client.claim_reverse_swap(
Expand All @@ -259,35 +263,38 @@ def claim_reverse_swap(

@click.command()
@click.argument("swap_id", type=str)
def swap_status(swap_id):
async def swap_status(swap_id):
"""
get swap status
retrieves the status of your boltz swap from the api

ID is the id of your boltz swap
"""
client = BoltzClient(config)
await client.init_pairs()
data = client.swap_status(swap_id)
click.echo(data)


@click.command()
@click.argument("amount", type=int)
def calculate_swap_send_amount(amount):
async def calculate_swap_send_amount(amount):
"""
calculate the amount of the invoice you have to send to boltz
to send the specified amount onchain
"""
client = BoltzClient(config)
await client.init_pairs()
click.echo(client.substract_swap_fees(amount))


@click.command()
def show_pairs():
async def show_pairs():
"""
show pairs of possible assets to swap
"""
client = BoltzClient(config)
await client.init_pairs()
data = client.get_pairs()
click.echo(json.dumps(data))

Expand Down
21 changes: 11 additions & 10 deletions boltz_client/helpers.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
"""boltz_client helpers"""

import httpx
from httpx import AsyncClient


def req_wrap(funcname, *args, **kwargs) -> dict:
async def req_wrap(funcname, *args, **kwargs) -> dict:
"""request wrapper for httpx"""
func = getattr(httpx, funcname)
res = func(*args, timeout=30, **kwargs)
res.raise_for_status()
return (
res.json()
if kwargs["headers"]["Content-Type"] == "application/json"
else {"text": res.text}
)
async with AsyncClient(follow_redirects=True) as client:
func = getattr(client, funcname)
res = await func(*args, timeout=30, **kwargs)
res.raise_for_status()
return (
res.json()
if kwargs["headers"]["Content-Type"] == "application/json"
else {"text": res.text}
)
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ urls = { Homepage = "https://lnbits.com", Repository = "https://github.com/lnbit
readme = "README.md"
dependencies = [ "lnbits>1" ]

[tool.uv]
dev-dependencies = [
[dependency-groups]
dev = [
"black",
"pytest-asyncio",
"pytest",
Expand Down
4 changes: 2 additions & 2 deletions tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ async def check_for_auto_swap(payment: Payment) -> None:
f"{auto_swap.feerate_limit}, actual fees: {fees}"
)
return
claim_privkey_wif, preimage_hex, swap = client.create_reverse_swap(
amount=int(amount)
claim_privkey_wif, preimage_hex, swap = (
await client.create_reverse_swap(amount=int(amount))
)
new_swap = await create_reverse_submarine_swap(
CreateReverseSubmarineSwap(
Expand Down
4 changes: 3 additions & 1 deletion utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ async def create_boltz_client(pair: str = "BTC/BTC") -> BoltzClient:
network=settings.boltz_network,
network_liquid=settings.boltz_network_liquid,
)
return BoltzClient(config, pair)
client = BoltzClient(config, pair)
await client.init_pairs()
return client


async def check_balance(data) -> bool:
Expand Down
4 changes: 2 additions & 2 deletions views_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ async def api_submarineswap_create(data: CreateSubmarineSwap) -> SubmarineSwap:
expiry=60 * 60 * 24, # 1 day
)
try:
refund_privkey_wif, swap = client.create_swap(payment.bolt11)
refund_privkey_wif, swap = await client.create_swap(payment.bolt11)
except Exception as exc:
logger.error(exc)
raise HTTPException(
Expand Down Expand Up @@ -281,7 +281,7 @@ async def api_reverse_submarineswap_create(
)

try:
claim_privkey_wif, preimage_hex, swap = client.create_reverse_swap(
claim_privkey_wif, preimage_hex, swap = await client.create_reverse_swap(
amount=amount,
)
except Exception as exc:
Expand Down
Loading