Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
7d26d89
Issue14, 25, 26
DoktorShift Jan 13, 2026
d907cc2
Avoid using literals, go with v-text, Get rid of delimiters on .js file
DoktorShift Jan 14, 2026
e978709
wording tooltip
DoktorShift Jan 14, 2026
551639c
wording empty dropdown
DoktorShift Jan 14, 2026
031b130
sync prob fixed, added ln address column to table
DoktorShift Jan 14, 2026
edc56e9
chore: lint
talvasconcelos Jan 23, 2026
454e80a
chore: lock
talvasconcelos Jan 23, 2026
e192531
.
talvasconcelos Jan 23, 2026
22a1106
working on requested changes
DoktorShift Feb 3, 2026
f6735c7
All Wallets with all connections as default
DoktorShift Feb 3, 2026
3f8dc19
added nip-47 notifications
DoktorShift Feb 9, 2026
8292d52
txt changes
DoktorShift Feb 9, 2026
286fabe
tests
DoktorShift Feb 10, 2026
204fd32
fix: linter issues (black, ruff, prettier, type fixes)
DoktorShift Mar 25, 2026
c79a1a0
fix: exclude standalone test mocks from type checkers
DoktorShift Mar 25, 2026
017ae58
fix: move standalone tests to tests/standalone/ for mypy compat
DoktorShift Mar 25, 2026
c333590
fix: reformat after rebase + move standalone tests for mypy compat
DoktorShift Mar 25, 2026
d7b056e
fix: sync uv.lock with main to fix mypy internal error
DoktorShift Mar 25, 2026
0c7424c
fix: reformat with black 25.1.0 to match lock file version
DoktorShift Mar 25, 2026
47991c4
fix: remove pyrightconfig.json that broke pyright in CI
DoktorShift Mar 25, 2026
bec270a
fix: add pyright inline ignores for standalone test mocks
DoktorShift Mar 25, 2026
f5ffd0c
fix: decode URL-encoded relay in test client
DoktorShift Mar 25, 2026
c1c6d0d
Merge upstream/main into nip47_notifications
DoktorShift Jun 2, 2026
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ data
.vscode
dump
.venv
.claude
1 change: 1 addition & 0 deletions crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ async def create_nwc(data: CreateNWCKey) -> NWCKey:
permissions=" ".join(data.permissions),
created_at=int(time.time()),
last_used=int(time.time()),
lud16=data.lud16,
)
await db.insert("nwcprovider.keys", nwckey_entry)
if data.budgets:
Expand Down
7 changes: 7 additions & 0 deletions migrations.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,10 @@ async def m006_default_config3(db):
""",
{"value": "0"},
)


async def m007_add_lud16(db):
"""
Add lud16 column to keys table for lightning address support
"""
await db.execute("ALTER TABLE nwcprovider.keys ADD COLUMN lud16 TEXT")
10 changes: 10 additions & 0 deletions models.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class NWCKey(BaseModel):
permissions: str
created_at: int
last_used: int
lud16: str | None = None

def get_permissions(self) -> list[str]:
try:
Expand Down Expand Up @@ -67,6 +68,7 @@ class CreateNWCKey(BaseModel):
expires_at: int
permissions: list[str]
budgets: list[NWCNewBudget] | None = None
lud16: str | None = None


class DeleteNWC(BaseModel):
Expand Down Expand Up @@ -102,13 +104,21 @@ class NWCRegistrationRequest(BaseModel):
description: str
expires_at: int
budgets: list[NWCNewBudget]
lud16: str | None = None


class NWCGetResponse(BaseModel):
data: NWCKey
budgets: list[NWCBudget]


class NWCGetAllResponse(BaseModel):
data: NWCKey
budgets: list[NWCBudget]
wallet_id: str
wallet_name: str


class NWCConfig(BaseModel):
key: str
value: str
37 changes: 36 additions & 1 deletion nwcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,12 @@ def __init__(
# List of supported methods
self.supported_methods: list[str] = []

# List of supported notification types
self.supported_notifications: list[str] = [
"payment_received",
"payment_sent",
]

# Keep track of the number of subscriptions (used for unique subid)
self.subscriptions_count: int = 0

Expand Down Expand Up @@ -308,11 +314,14 @@ async def _send_info_event(self):
"""
Build and publish the NWC service info event (kind 13194).
"""
tags = [["p", self.public_key_hex]]
if self.supported_notifications:
tags.append(["notifications", " ".join(self.supported_notifications)])
event = {
"kind": 13194,
"content": " ".join(self.supported_methods),
"created_at": int(time.time()),
"tags": [["p", self.public_key_hex]],
"tags": tags,
}
self._sign_event(event)
await self._send(["EVENT", event])
Expand Down Expand Up @@ -415,6 +424,32 @@ def _extract_expiration_from_tags(self, tags: list) -> int:
pass
return expiration

async def send_notification(
self, client_pubkey: str, notification_type: str, notification: dict
):
"""
Send a NIP-47 notification event (kind 23196) to a client.

Args:
client_pubkey: The client's public key hex.
notification_type: The notification type (e.g. "payment_received").
notification: The notification payload dict.
"""
content = self._json_dumps(
{
"notification_type": notification_type,
"notification": notification,
}
)
event: dict = {
"kind": 23196,
"created_at": int(time.time()),
"tags": [["p", client_pubkey]],
"content": self.private_key.encrypt_message(content, client_pubkey),
}
self._sign_event(event)
await self._send(["EVENT", event])

async def _on_event_message(self, msg):
if not self.sub:
return
Expand Down
5 changes: 5 additions & 0 deletions permission.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,9 @@
"default": True,
},
"info": {"name": "Read account info", "methods": ["get_info"], "default": True},
"notifications": {
"name": "Receive payment notifications",
"methods": [],
"default": True,
},
}
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ package-mode = false

[tool.mypy]
plugins = ["pydantic.mypy"]
exclude = [
"tests/standalone/",
]

[[tool.mypy.overrides]]
module = [
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added static/image/NWC_Banner_Extension.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion static/js/admin.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
window.app = Vue.createApp({
el: '#vue',
mixins: [windowMixin],
delimiters: ['${', '}'],
data: function () {
return {
config: {},
Expand Down
Loading
Loading