diff --git a/api/logics.py b/api/logics.py
index da562bc4..8bec242a 100644
--- a/api/logics.py
+++ b/api/logics.py
@@ -8,7 +8,7 @@ from django.utils import timezone
from api.lightning.node import LNNode
from api.models import Currency, LNPayment, MarketTick, OnchainPayment, Order
-from api.tasks import send_devfund_donation, send_notification, send_order_nostr_event
+from api.tasks import send_devfund_donation, send_notification, nostr_send_order_event
from api.utils import get_minning_fee, validate_onchain_address, location_country
from chat.models import Message
@@ -704,9 +704,9 @@ class Logics:
if context["invoice_amount"] < MIN_SWAP_AMOUNT:
context["swap_allowed"] = False
- context[
- "swap_failure_reason"
- ] = f"Order amount is smaller than the minimum swap available of {MIN_SWAP_AMOUNT} Sats"
+ context["swap_failure_reason"] = (
+ f"Order amount is smaller than the minimum swap available of {MIN_SWAP_AMOUNT} Sats"
+ )
order.log(
f"Onchain payment option was not offered: amount is smaller than the minimum swap available of {MIN_SWAP_AMOUNT} Sats",
level="WARN",
@@ -714,9 +714,9 @@ class Logics:
return True, context
elif context["invoice_amount"] > MAX_SWAP_AMOUNT:
context["swap_allowed"] = False
- context[
- "swap_failure_reason"
- ] = f"Order amount is bigger than the maximum swap available of {MAX_SWAP_AMOUNT} Sats"
+ context["swap_failure_reason"] = (
+ f"Order amount is bigger than the maximum swap available of {MAX_SWAP_AMOUNT} Sats"
+ )
order.log(
f"Onchain payment option was not offered: amount is bigger than the maximum swap available of {MAX_SWAP_AMOUNT} Sats",
level="WARN",
@@ -741,9 +741,9 @@ class Logics:
)
if not valid:
context["swap_allowed"] = False
- context[
- "swap_failure_reason"
- ] = "Not enough onchain liquidity available to offer a swap"
+ context["swap_failure_reason"] = (
+ "Not enough onchain liquidity available to offer a swap"
+ )
order.log(
"Onchain payment option was not offered: onchain liquidity available to offer a swap",
level="WARN",
@@ -1019,6 +1019,8 @@ class Logics:
order.log("Order expired while waiting for maker bond")
order.log("Maker bond was cancelled")
+ nostr_send_order_event.delay(order_id=order.id)
+
return True, None
# 2.a) When maker cancels after bond
@@ -1039,6 +1041,8 @@ class Logics:
order.log("Order cancelled by maker while public or paused")
order.log("Maker bond was unlocked")
+ nostr_send_order_event.delay(order_id=order.id)
+
return True, None
# 2.b) When maker cancels after bond and before taker bond is locked
@@ -1058,6 +1062,8 @@ class Logics:
order.log("Maker bond was unlocked")
order.log("Taker bond was cancelled")
+ nostr_send_order_event.delay(order_id=order.id)
+
return True, None
# 3) When taker cancels before bond
@@ -1070,6 +1076,8 @@ class Logics:
order.log("Taker cancelled before locking the bond")
+ nostr_send_order_event.delay(order_id=order.id)
+
return True, None
# 4) When taker or maker cancel after bond (before escrow)
@@ -1099,6 +1107,8 @@ class Logics:
order.log("Maker bond was settled")
order.log("Taker bond was unlocked")
+ nostr_send_order_event.delay(order_id=order.id)
+
return True, None
# 4.b) When taker cancel after bond (before escrow)
@@ -1121,6 +1131,8 @@ class Logics:
order.log("Taker bond was settled")
order.log("Maker bond was unlocked")
+ nostr_send_order_event.delay(order_id=order.id)
+
return True, None
# 5) When trade collateral has been posted (after escrow)
@@ -1136,6 +1148,9 @@ class Logics:
order.log(
f"Taker Robot({user.robot.id},{user.username}) accepted the collaborative cancellation"
)
+
+ nostr_send_order_event.delay(order_id=order.id)
+
return True, None
# if the taker had asked, and now the maker does: cancel order, return everything
@@ -1144,6 +1159,9 @@ class Logics:
order.log(
f"Maker Robot({user.robot.id},{user.username}) accepted the collaborative cancellation"
)
+
+ nostr_send_order_event.delay(order_id=order.id)
+
return True, None
# Otherwise just make true the asked for cancel flags
@@ -1181,6 +1199,8 @@ class Logics:
order.update_status(Order.Status.CCA)
send_notification.delay(order_id=order.id, message="collaborative_cancelled")
+ nostr_send_order_event.delay(order_id=order.id)
+
order.log("Order was collaboratively cancelled")
order.log("Maker bond was unlocked")
order.log("Taker bond was unlocked")
@@ -1208,7 +1228,7 @@ class Logics:
order.save() # update all fields
- send_order_nostr_event.delay(order_id=order.id, message="new")
+ nostr_send_order_event.delay(order_id=order.id)
order.log(f"Order({order.id},{str(order)}) is public in the order book")
return
@@ -1352,6 +1372,9 @@ class Logics:
except Exception:
pass
send_notification.delay(order_id=order.id, message="order_taken_confirmed")
+
+ nostr_send_order_event.delay(order_id=order.id)
+
order.log(
f"Contract formalized. Maker: Robot({order.maker.robot.id},{order.maker}). Taker: Robot({order.taker.robot.id},{order.taker}). API median price {order.currency.exchange_rate} {dict(Currency.currency_choices)[order.currency.currency]}/BTC. Premium is {order.premium}%. Contract size {order.last_satoshis} Sats"
)
@@ -1743,11 +1766,15 @@ class Logics:
order.log(
f"Robot({user.robot.id},{user.username}) paused the public order"
)
+
+ nostr_send_order_event.delay(order_id=order.id)
elif order.status == Order.Status.PAU:
order.update_status(Order.Status.PUB)
order.log(
f"Robot({user.robot.id},{user.username}) made public the paused order"
)
+
+ nostr_send_order_event.delay(order_id=order.id)
else:
order.log(
f"Robot({user.robot.id},{user.username}) tried to pause/unpause an order that was not public or paused",
diff --git a/api/nostr.py b/api/nostr.py
index 02effa65..4ebb5460 100644
--- a/api/nostr.py
+++ b/api/nostr.py
@@ -1,4 +1,6 @@
import pygeohash
+import hashlib
+import uuid
from nostr_sdk import Keys, Client, EventBuilder, NostrSigner
from api.models import Order
from decouple import config
@@ -7,7 +9,7 @@ from decouple import config
class Nostr:
"""Simple nostr events manager to be used as a cache system for clients"""
- async def send_new_order_event(self, order):
+ async def send_order_event(self, order):
"""Creates the event and sends it to the coordinator relay"""
# Initialize with coordinator Keys
keys = Keys.generate()
@@ -23,12 +25,16 @@ class Nostr:
print(f"Nostr event sent: {output}")
def generate_tags(self, order):
+ hashed_id = hashlib.md5(
+ f"{config("COORDINATOR_ALIAS", cast=str)}{order.id}".encode("utf-8")
+ ).hexdigest()
+
return [
- ["d", order.id],
+ ["d", uuid.UUID(hashed_id)],
["name", order.maker.robot_name],
["k", order.type.lower()],
["f", order.currency],
- ["s", Order.Status(order.status).label],
+ ["s", self.get_status_tag(order)],
["amt", order.last_satoshis],
["fa", order.amount],
["pm", order.payment_method.split(" ")],
@@ -39,9 +45,16 @@ class Nostr:
],
["expiration", order.expires_at.timestamp()],
["y", "robosats"],
- ["coordinator", config("COORDINATOR_ALIAS", cast=str)]["z", "order"],
["n", order.network],
["layer", "lightning"],
["g", pygeohash.encode(order.latitude, order.longitude)],
["bond", order.bond],
+ ["z", "order"],
+ ["coordinator", config("COORDINATOR_ALIAS", cast=str)],
]
+
+ def get_status_tag(self, order):
+ if order.status == Order.Status.PUB:
+ return "pending"
+ else:
+ return "canceled"
diff --git a/api/tasks.py b/api/tasks.py
index fa1d44b7..a87c1f00 100644
--- a/api/tasks.py
+++ b/api/tasks.py
@@ -253,7 +253,7 @@ def cache_market():
@shared_task(name="", ignore_result=True, time_limit=120)
-def send_order_nostr_event(order_id=None, message=None):
+def nostr_send_order_event(order_id=None):
if order_id:
from api.models import Order
from api.nostr import Nostr
@@ -261,8 +261,7 @@ def send_order_nostr_event(order_id=None, message=None):
order = Order.objects.get(id=order_id)
nostr = Nostr()
- if message == "new":
- coroutine = nostr.send_new_order_event(order)
+ coroutine = nostr.send_order_event(order)
if coroutine:
loop = asyncio.get_event_loop()
loop.run_until_complete(coroutine)
diff --git a/docker-tests.yml b/docker-tests.yml
index 41d3b5aa..21de001f 100644
--- a/docker-tests.yml
+++ b/docker-tests.yml
@@ -205,14 +205,14 @@ services:
# - redis
# network_mode: service:bitcoind
- # strfry:
- # build: https://github.com/hoytech/strfry.git
- # container_name: test-strfry
- # restart: unless-stopped
- # volumes:
- # - ./nodeapp/strfry/config/strfry.conf:/strfry/strfry.conf:r
- # network_mode: service:bitcoind
-
+ strfry:
+ image: dockurr/strfry:0.9.6
+ container_name: test-strfry
+ restart: unless-stopped
+ volumes:
+ - ./nodeapp/strfry/db:/app/strfry-db
+ - ./nodeapp/strfry/config/strfry.conf:/etc/strfry.conf:r
+ network_mode: service:bitcoind
volumes:
redisdata:
diff --git a/requirements.txt b/requirements.txt
index 6e7ae047..e6200b67 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -29,3 +29,4 @@ drf-spectacular-sidecar==2024.7.1
django-cors-headers==4.4.0
base91==1.0.1
nostr-sdk==0.32.2
+pygeohash==1.2.0
diff --git a/tests/utils/trade.py b/tests/utils/trade.py
index 7ac981b7..c6303b0a 100644
--- a/tests/utils/trade.py
+++ b/tests/utils/trade.py
@@ -5,7 +5,7 @@ from django.urls import reverse
from api.management.commands.clean_orders import Command as CleanOrders
from api.management.commands.follow_invoices import Command as FollowInvoices
from api.models import Order
-from api.tasks import follow_send_payment, send_notification, send_order_nostr_event
+from api.tasks import follow_send_payment, send_notification, nostr_send_order_event
from tests.utils.node import (
add_invoice,
create_address,
@@ -156,7 +156,7 @@ class Trade:
wait_nodes_sync()
@patch("api.tasks.send_notification.delay", send_notification)
- @patch("api.tasks.send_order_nostr_event.delay", send_order_nostr_event)
+ @patch("api.tasks.nostr_send_order_event.delay", nostr_send_order_event)
def publish_order(self):
# Maker's first order fetch. Should trigger maker bond hold invoice generation.
self.get_order()