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
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
<ProjectReference Include="..\..\submodules\btcpayserver\BTCPayServer\BTCPayServer.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="NNostr.Client" Version="0.0.54" />
<PackageReference Include="NNostr.Client" Version="0.0.55" />
</ItemGroup>
<ItemGroup>
<Folder Include="Resources" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using BTCPayServer.Payments.Lightning;
using NBitcoin;
using NBitcoin.Secp256k1;
using Microsoft.Extensions.Logging;
using NNostr.Client;
using NNostr.Client.Protocols;
using SHA256 = System.Security.Cryptography.SHA256;
Expand All @@ -32,11 +33,15 @@ public class NostrWalletConnectLightningClient : IExtendedLightningClient
private readonly (ECXOnlyPubKey pubkey, ECPrivKey secret, Uri[] relays, string lud16) _connectParams;
private readonly Uri? _serverUri;

public NostrWalletConnectLightningClient(NostrClientPool nostrClientPool, Uri uri, Network network)
private readonly ILogger<NostrWalletConnectLightningClient>? _logger;

public NostrWalletConnectLightningClient(NostrClientPool nostrClientPool, Uri uri, Network network,
ILogger<NostrWalletConnectLightningClient>? logger = null)
{
_nostrClientPool = nostrClientPool;
_uri = uri;
_network = network;
_logger = logger;
_connectParams = NIP47.ParseUri(uri);
_serverUri = _connectParams.relays.FirstOrDefault();
}
Expand All @@ -46,6 +51,25 @@ public override string ToString()
return $"type=nwc;key={_uri}";
}

private NIP47.EncryptionScheme? _encryptionScheme;

// NIP-47: the wallet service advertises supported encryption schemes via the "encryption"
// tag on its kind-13194 info event. Prefer nip44_v2 when available, otherwise fall back
// to nip04 (absence of the tag means the wallet only supports nip04).
private async Task<NIP47.EncryptionScheme> GetEncryptionScheme(INostrClient client, CancellationToken cancellationToken)
{
if (_encryptionScheme is { } cached)
return cached;
var info = await client.FetchNIP47InfoEvent(_connectParams.pubkey, cancellationToken);
var scheme = info?.EncryptionSchemes?.Contains(NIP47.EncryptionSchemeNip44V2) is true
? NIP47.EncryptionScheme.Nip44V2
: NIP47.EncryptionScheme.Nip04;
Comment on lines +63 to +66

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 3 'NNostr\.Client|PackageReference Include="NNostr\.Client"|PackageVersion Include="NNostr\.Client"' \
  -g '*.csproj' -g 'Directory.Packages.props' -g 'packages.props'

Repository: Kukks/BTCPayServerPlugins

Length of output: 163


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- matching project and package files ---'
git ls-files '*.csproj' 'Directory.Packages.props' 'packages.props' | sort
printf '%s\n' '--- NNostr references and API usage ---'
rg -n -i -C 3 'NNostr|Nip44V2|EncryptionSchemeNip44V2|FetchNIP47InfoEvent' \
  -g '*.cs' -g '*.csproj' -g '*.props' -g '*.targets' -g '*.sln' .
printf '%s\n' '--- target file context ---'
target=$(git ls-files | grep -F 'NostrWalletConnectLightningClient.cs' | head -n 1 || true)
if [ -n "$target" ]; then
  cat -n "$target" | sed -n '1,130p'
else
  printf '%s\n' 'Target file not found'
fi

Repository: Kukks/BTCPayServerPlugins

Length of output: 25506


🏁 Script executed:

#!/bin/bash
set -euo pipefail

tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT

url='https://api.nuget.org/v3-flatcontainer/nnstr.client/0.0.54/nnstr.client.0.0.54.nupkg'
curl -fsSL "$url" -o "$tmp/package.nupkg"
printf '%s\n' '--- package contents ---'
unzip -l "$tmp/package.nupkg"
printf '%s\n' '--- package metadata ---'
unzip -p "$tmp/package.nupkg" '*.nuspec' | sed -n '1,160p'
printf '%s\n' '--- API symbol strings ---'
unzip -p "$tmp/package.nupkg" 'lib/**/*.xml' 2>/dev/null | \
  rg -n 'Nip44|FetchNIP47InfoEvent|EncryptionScheme|SendNIP47Request' || true

Repository: Kukks/BTCPayServerPlugins

Length of output: 214


🌐 Web query:

NNostr.Client NuGet 0.0.54 NIP-44 EncryptionScheme Nip44V2

💡 Result:

As of August 12, 2026, the NNostr.Client NuGet package (maintained by Kukks) is at version 0.0.54 [1]. While the project is a widely used C# Nostr client [2][3], there is no explicit documentation or public changelog confirming native implementation or support for NIP-44 (specifically Nip44V2 encryption) within this specific library version [2][4]. NIP-44 is the standard for versioned encrypted payloads in Nostr, utilizing ChaCha20-Poly1305 and HKDF for key derivation, effectively succeeding the older NIP-04 encryption [5][6]. Developers requiring NIP-44 functionality in C# often look toward alternative libraries or manual implementations that follow the NIP-44 specification [5]. If you are implementing NIP-44, the specification requires the use of HKDF-extract and HKDF-expand to derive conversation keys, and the ChaCha20 stream cipher as defined in the official NIP-44 documentation [5]. If you are using NNostr.Client and require NIP-44 support, you should check the official GitHub repository's issue tracker or pull requests, as support for newer NIPs is often added via community contributions [2][4][7]. Users requiring more comprehensive NIP coverage, such as those found in the Nostr.Client library (by Marfusios), may find different levels of protocol support, though you should verify specific NIP-44 support there as well [8][9].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- NuGet package search ---'
curl -fsSLG 'https://azuresearch-usnc.nuget.org/query' \
  --data-urlencode 'q=NNostr.Client' \
  --data-urlencode 'prerelease=false' |
  python3 -c '
import json, sys
data=json.load(sys.stdin)
for item in data.get("data", []):
    print(item.get("id"), item.get("version"), item.get("versions"))
'
printf '%s\n' '--- NuGet registration candidates ---'
for id in nnostr.client nnstr.client nn-ostr.client; do
  url="https://api.nuget.org/v3-registration5-gz-semver2/${id}/index.json"
  printf '%s: ' "$id"
  curl -sS -o /tmp/nuget-registration.json -w '%{http_code}\n' "$url"
  if [ -s /tmp/nuget-registration.json ]; then
    python3 - "$id" <<'PY'
import json, sys
try:
    data=json.load(open("/tmp/nuget-registration.json"))
    versions=[]
    for page in data.get("items", []):
        for item in page.get("items", []) or []:
            catalog=item.get("catalogEntry", {})
            versions.append(catalog.get("version"))
    print(", ".join(v for v in versions if v and (v == "0.0.54" or v.startswith("0.0.")))[-500:])
except Exception as e:
    print(e)
PY
  fi
done

Repository: Kukks/BTCPayServerPlugins

Length of output: 7293


🏁 Script executed:

#!/bin/bash
set -euo pipefail

tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT

url='https://api.nuget.org/v3-flatcontainer/nnostr.client/0.0.54/nnostr.client.0.0.54.nupkg'
curl -fsSL "$url" -o "$tmp/package.nupkg"
printf '%s\n' '--- package contents ---'
unzip -l "$tmp/package.nupkg"
printf '%s\n' '--- package metadata ---'
unzip -p "$tmp/package.nupkg" '*.nuspec' | sed -n '1,160p'
printf '%s\n' '--- API symbol strings in XML docs ---'
unzip -p "$tmp/package.nupkg" 'lib/**/*.xml' 2>/dev/null | \
  rg -n 'Nip44|FetchNIP47InfoEvent|EncryptionScheme|SendNIP47Request' || true
printf '%s\n' '--- API symbol strings in binaries ---'
unzip -p "$tmp/package.nupkg" 'lib/**/*.dll' 2>/dev/null | \
  strings | rg -n 'Nip44|FetchNIP47InfoEvent|EncryptionScheme|SendNIP47Request' || true

Repository: Kukks/BTCPayServerPlugins

Length of output: 4223


🏁 Script executed:

#!/bin/bash
set -euo pipefail

tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT

curl -fsSL \
  'https://github.com/Kukks/NNostr/archive/45dee385b2527bcf8763674fcf3ca283aee71ffd.tar.gz' \
  -o "$tmp/source.tar.gz"
tar -xzf "$tmp/source.tar.gz" -C "$tmp"
src=$(find "$tmp" -maxdepth 1 -type d -name 'NNostr-*' | head -n 1)

printf '%s\n' '--- source revision ---'
rg -n -i -C 3 'Nip44|FetchNIP47InfoEvent|EncryptionScheme|SendNIP47Request' "$src" \
  -g '*.cs' -g '*.csproj' -g '*.md' || true
printf '%s\n' '--- NIP-47 and encryption declarations ---'
rg -n -i -C 5 'class NIP47|enum EncryptionScheme|EncryptionSchemeNip04|EncryptionSchemeNip44V2|SendNIP47Request' "$src" \
  -g '*.cs' || true

Repository: Kukks/BTCPayServerPlugins

Length of output: 42596


🏁 Script executed:

#!/bin/bash
set -euo pipefail

tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT

curl -fsSL \
  'https://github.com/Kukks/NNostr/archive/45dee385b2527bcf8763674fcf3ca283aee71ffd.tar.gz' \
  -o "$tmp/source.tar.gz"
tar -xzf "$tmp/source.tar.gz" -C "$tmp"
src=$(find "$tmp" -maxdepth 1 -type d -name 'NNostr-*' | head -n 1)

printf '%s\n' '--- NIP-47 implementation in the 0.0.54 source revision ---'
sed -n '110,210p' "$src/NNostr.Client/Protocols/NIP47.cs"
printf '%s\n' '--- all requested symbols ---'
rg -n -i 'FetchNIP47InfoEvent|EncryptionSchemes|EncryptionSchemeNip44V2|enum EncryptionScheme|SendNIP47Request' \
  "$src/NNostr.Client" -g '*.cs' || true
printf '%s\n' '--- package reference locations ---'
rg -n -C 2 'PackageReference Include="NNostr\.Client"' \
  -g '*.csproj' -g '*.props' -g '*.targets' .

Repository: Kukks/BTCPayServerPlugins

Length of output: 7145


Update the NNostr.Client package reference before using the new NIP-47 APIs.

Version 0.0.54 lacks FetchNIP47InfoEvent, NIP47.EncryptionSchemeNip44V2, and the SendNIP47Request overload that accepts an encryption scheme. The project will not compile until the reference targets a release that provides these APIs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Plugins/BTCPayServer.Plugins.NIP05/NostrWalletConnectLightningClient.cs`
around lines 63 - 66, Update the project’s NNostr.Client package reference to a
release that provides FetchNIP47InfoEvent, NIP47.EncryptionSchemeNip44V2, and
the SendNIP47Request overload accepting an encryption scheme, ensuring the
existing NostrWalletConnectLightningClient usage compiles.

_encryptionScheme = scheme;
_logger?.LogInformation("NWC: negotiated {Scheme} encryption with wallet service on {Relay} (advertised: {Advertised})",
scheme, _serverUri, info?.EncryptionSchemes is { Length: > 0 } s ? string.Join(" ", s) : "(no encryption tag)");
return scheme;
}


public async Task<LightningInvoice> GetInvoice(string invoiceId,
CancellationToken cancellation = new())
Expand Down Expand Up @@ -94,12 +118,13 @@ public async Task<LightningInvoice> GetInvoice(uint256 paymentHash,

using (usage)
{
var scheme = await GetEncryptionScheme(nostrClient, cts.Token);
var tx = await nostrClient.SendNIP47Request<NIP47.Nip47Transaction>(_connectParams.pubkey,
_connectParams.secret,
new NIP47.LookupInvoiceRequest()
{
PaymentHash = paymentHash.ToString()
}, cts.Token);
}, cts.Token, scheme);
return ToLightningInvoice(tx, _network)!;
}
}
Expand All @@ -118,14 +143,15 @@ public async Task<LightningInvoice[]> ListInvoices(ListInvoicesParams request,
var (client, usage) = await _nostrClientPool.GetClientAndConnect(_connectParams.relays, cts.Token);
using (usage)
{
var scheme = await GetEncryptionScheme(client, cts.Token);
var response = await client.SendNIP47Request<NIP47.ListTransactionsResponse>(_connectParams.pubkey,
_connectParams.secret,
new NIP47.ListTransactionsRequest()
{
Type = "incoming",
Offset = (int)(request.OffsetIndex ?? 0),
Unpaid = request.PendingOnly ?? false,
}, cts.Token);
}, cts.Token, scheme);

return response.Transactions.Select(transaction => ToLightningInvoice(transaction, _network))
.Where(i => i is not null).ToArray()!;
Expand Down Expand Up @@ -177,11 +203,12 @@ public async Task<LightningPayment> GetPayment(string paymentHash,
NIP47.Nip47Transaction tx;
try
{
var scheme = await GetEncryptionScheme(nostrClient, cts.Token);
tx = await nostrClient.SendNIP47Request<NIP47.Nip47Transaction>(_connectParams.pubkey, _connectParams.secret,
new NIP47.LookupInvoiceRequest()
{
PaymentHash = paymentHash
}, cts.Token);
}, cts.Token, scheme);
}
// The standard says it returns NOT_FOUND error, but
// Alby returns INTERNAL error... Probably safer to catch all
Expand All @@ -207,14 +234,15 @@ public async Task<LightningPayment[]> ListPayments(ListPaymentsParams request,

using (usage)
{
var scheme = await GetEncryptionScheme(nostrClient, cts.Token);
var response = await nostrClient.SendNIP47Request<NIP47.ListTransactionsResponse>(_connectParams.pubkey,
_connectParams.secret,
new NIP47.ListTransactionsRequest()
{
Type = "outgoing",
Offset = (int)(request.OffsetIndex ?? 0),
Unpaid = request.IncludePending ?? false,
}, cts.Token);
}, cts.Token, scheme);
return response.Transactions.Select(ToLightningPayment).Where(i => i is not null).ToArray()!;
}
}
Expand All @@ -235,6 +263,7 @@ public async Task<LightningInvoice> CreateInvoice(CreateInvoiceParams createInvo

using (usage)
{
var scheme = await GetEncryptionScheme(nostrClient, cts.Token);
var response = await nostrClient.SendNIP47Request<NIP47.Nip47Transaction>(_connectParams.pubkey,
_connectParams.secret,
new NIP47.MakeInvoiceRequest()
Expand All @@ -245,24 +274,30 @@ public async Task<LightningInvoice> CreateInvoice(CreateInvoiceParams createInvo
: createInvoiceRequest.Description,
DescriptionHash = createInvoiceRequest.DescriptionHash?.ToString(),
ExpirySeconds = (int)createInvoiceRequest.Expiry.TotalSeconds,
}, cts.Token);
}, cts.Token, scheme);
return ToLightningInvoice(response, _network)!;
}
}

public async Task<ILightningInvoiceListener> Listen(CancellationToken cancellation = default)
{
var x = await _nostrClientPool.GetClientAndConnect(_connectParams.relays, cancellation);
var commands = await x.Item1.FetchNIP47AvailableCommands(_connectParams.Item1, cancellationToken: cancellation);
bool? hasNotification = commands?.Notifications?.Contains("payment_received");
var info = await x.Item1.FetchNIP47InfoEvent(_connectParams.Item1, cancellationToken: cancellation);
var scheme = info?.EncryptionSchemes?.Contains(NIP47.EncryptionSchemeNip44V2) is true
? NIP47.EncryptionScheme.Nip44V2
: NIP47.EncryptionScheme.Nip04;
_encryptionScheme = scheme;
bool? hasNotification = info?.Notifications?.Contains("payment_received");
if (hasNotification is false)
{
var response = await x.Item1.SendNIP47Request<NIP47.GetInfoResponse>(_connectParams.pubkey, _connectParams.secret, new NIP47.GetInfoRequest(), cancellationToken: cancellation);
var response = await x.Item1.SendNIP47Request<NIP47.GetInfoResponse>(_connectParams.pubkey, _connectParams.secret, new NIP47.GetInfoRequest(), cancellationToken: cancellation, encryptionScheme: scheme);
hasNotification = response?.Notifications?.Contains("payment_received");
}
Comment on lines +290 to 295

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Query get_info when the info event has no notification capability.

hasNotification is null when the info event or its Notifications field is absent. The current is false condition skips the encrypted get_info fallback and selects polling even when the wallet advertises payment_received through get_info. Use is not true.

Proposed fix
-        if (hasNotification is false)
+        if (hasNotification is not true)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
bool? hasNotification = info?.Notifications?.Contains("payment_received");
if (hasNotification is false)
{
var response = await x.Item1.SendNIP47Request<NIP47.GetInfoResponse>(_connectParams.pubkey, _connectParams.secret, new NIP47.GetInfoRequest(), cancellationToken: cancellation);
var response = await x.Item1.SendNIP47Request<NIP47.GetInfoResponse>(_connectParams.pubkey, _connectParams.secret, new NIP47.GetInfoRequest(), cancellationToken: cancellation, encryptionScheme: scheme);
hasNotification = response?.Notifications?.Contains("payment_received");
}
bool? hasNotification = info?.Notifications?.Contains("payment_received");
if (hasNotification is not true)
{
var response = await x.Item1.SendNIP47Request<NIP47.GetInfoResponse>(_connectParams.pubkey, _connectParams.secret, new NIP47.GetInfoRequest(), cancellationToken: cancellation, encryptionScheme: scheme);
hasNotification = response?.Notifications?.Contains("payment_received");
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Plugins/BTCPayServer.Plugins.NIP05/NostrWalletConnectLightningClient.cs`
around lines 290 - 295, Update the condition in the notification capability
check around hasNotification so the get_info fallback executes whenever
payment_received has not been confirmed, including when the value is null.
Replace the false-only check with an is-not-true check, preserving the existing
request and response handling.

_logger?.LogInformation("NWC: listening for paid invoices via {Listener} ({Scheme}, payment_received supported: {HasNotification})",
hasNotification is true ? "push notifications" : "polling", scheme, hasNotification ?? false);
return hasNotification is true
? new NotificationListener(_network, x, _connectParams)
: new PollListener(_network, x, _connectParams);
? new NotificationListener(_network, x, _connectParams, scheme)
: new PollListener(_network, x, _connectParams, scheme);
Comment on lines 284 to +300

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Dispose the connection usage lease if listener initialization fails.

x.Item2 is transferred to a listener only on the return path. If FetchNIP47InfoEvent or SendNIP47Request throws first, the lease is not disposed. Dispose x.Item2 in a catch block before rethrowing.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Plugins/BTCPayServer.Plugins.NIP05/NostrWalletConnectLightningClient.cs`
around lines 284 - 300, Update the listener initialization flow around
GetClientAndConnect, FetchNIP47InfoEvent, and SendNIP47Request to catch
initialization failures, dispose x.Item2 before rethrowing, and preserve the
existing transfer of the lease to NotificationListener or PollListener on
successful return.

}


Expand All @@ -276,13 +311,14 @@ public class NotificationListener : ILightningInvoiceListener
private readonly IDisposable _disposable;

public NotificationListener(Network network, (INostrClient, IDisposable) client,
(ECXOnlyPubKey pubkey, ECPrivKey secret, Uri[] relays, string lud16) x)
(ECXOnlyPubKey pubkey, ECPrivKey secret, Uri[] relays, string lud16) x,
NIP47.EncryptionScheme encryptionScheme)
{
_network = network;
_client = client.Item1;
_disposable = client.Item2;
_cts = new CancellationTokenSource();
_notifications = _client.SubscribeNip47Notifications(x.pubkey, x.secret, _cts.Token);
_notifications = _client.SubscribeNip47Notifications(x.pubkey, x.secret, _cts.Token, encryptionScheme);
}

public void Dispose()
Expand Down Expand Up @@ -319,13 +355,17 @@ public class PollListener : ILightningInvoiceListener
private NIP47.ListTransactionsResponse? _lastPaid;
private Channel<LightningInvoice> queue = Channel.CreateUnbounded<LightningInvoice>();

private readonly NIP47.EncryptionScheme _encryptionScheme;

public PollListener(Network network, (INostrClient, IDisposable) client,
(ECXOnlyPubKey pubkey, ECPrivKey secret, Uri[] relays, string lud16) connectparams)
(ECXOnlyPubKey pubkey, ECPrivKey secret, Uri[] relays, string lud16) connectparams,
NIP47.EncryptionScheme encryptionScheme)
{
_network = network;
_connectparams = connectparams;
_client = client.Item1;
_disposable = client.Item2;
_encryptionScheme = encryptionScheme;
_cts = new CancellationTokenSource();
_ = Poll();
}
Expand All @@ -343,9 +383,9 @@ private async Task Poll()
_connectparams.secret, new NIP47.ListTransactionsRequest()
{
Type = "incoming",
// Unpaid = true, //seems like this is ignored... so we only get paid ones
// Unpaid = true, //seems like this is ignored... so we only get paid ones
Limit = 300
}, cancellationToken: cts.Token);
}, cancellationToken: cts.Token, encryptionScheme: _encryptionScheme);
paid.Transactions = paid.Transactions.Where(i => i is { Type: "incoming", SettledAt: not null }).ToArray();
if (_lastPaid is not null)
{
Expand Down Expand Up @@ -402,9 +442,10 @@ public async Task<LightningInvoice> WaitInvoice(CancellationToken cancellation)

using (usage)
{
var scheme = await GetEncryptionScheme(client, cts.Token);
var response = await client.SendNIP47Request<NIP47.GetBalanceResponse>(_connectParams.pubkey,
_connectParams.secret,
new NIP47.NIP47Request("get_balance"), cts.Token);
new NIP47.NIP47Request("get_balance"), cts.Token, scheme);
return new LightningNodeBalance()
{
OffchainBalance = new OffchainBalance()
Expand Down Expand Up @@ -456,7 +497,8 @@ public async Task<PayResponse> Pay(string bolt11, PayInvoiceParams payParams,
};

}
var response = await client.SendNIP47Request<NIP47.PayInvoiceResponse>(_connectParams.pubkey, _connectParams.secret, request, cts.Token);
var scheme = await GetEncryptionScheme(client, cts.Token);
var response = await client.SendNIP47Request<NIP47.PayInvoiceResponse>(_connectParams.pubkey, _connectParams.secret, request, cts.Token, scheme);
var payHash = ConvertHelper.ToHexString(SHA256.HashData(Convert.FromHexString(response.Preimage)));

try
Expand All @@ -465,7 +507,7 @@ public async Task<PayResponse> Pay(string bolt11, PayInvoiceParams payParams,
new NIP47.LookupInvoiceRequest()
{
PaymentHash = payHash
}, cts.Token);
}, cts.Token, scheme);
var lp = ToLightningPayment(tx)!;
return new PayResponse(lp.Status == LightningPaymentStatus.Complete ? PayResult.Ok : PayResult.Error,
new PayDetails()
Expand Down Expand Up @@ -530,16 +572,21 @@ public async Task<ConnectionResult> ConnectTo(NodeInfo nodeInfo,
var (client, disposable) = await _nostrClientPool.GetClientAndConnect(_connectParams.relays, cts.Token).ConfigureAwait(false);
using (disposable)
{
var commands = await client.FetchNIP47AvailableCommands(_connectParams.Item1, cancellationToken: cts.Token);
var info = await client.FetchNIP47InfoEvent(_connectParams.Item1, cancellationToken: cts.Token);
var requiredCommands = new[] { "get_info", "make_invoice", "lookup_invoice", "list_transactions" };
if (commands?.Commands is null || requiredCommands.Any(c => !commands.Value.Commands.Contains(c)))
if (info?.Commands is null || requiredCommands.Any(c => !info.Value.Commands.Contains(c)))
{
return new ValidationResult("No commands available or not all required commands are available (get_info, make_invoice, lookup_invoice, list_transactions)");
}

var scheme = info.Value.EncryptionSchemes?.Contains(NIP47.EncryptionSchemeNip44V2) is true
? NIP47.EncryptionScheme.Nip44V2
: NIP47.EncryptionScheme.Nip04;
_encryptionScheme = scheme;

var response = await client
.SendNIP47Request<NIP47.GetInfoResponse>(_connectParams.pubkey, _connectParams.secret,
new NIP47.GetInfoRequest(), cancellationToken: cts.Token);
new NIP47.GetInfoRequest(), cancellationToken: cts.Token, encryptionScheme: scheme);

var walletNetwork = response.Network;
if (!_network.ChainName.ToString().Equals(walletNetwork,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,13 @@ namespace BTCPayServer.Plugins.NIP05;
public class NostrWalletConnectLightningConnectionStringHandler : ILightningConnectionStringHandler
{
private readonly NostrClientPool _nostrClientPool;
private readonly Microsoft.Extensions.Logging.ILogger<NostrWalletConnectLightningClient> _logger;

public NostrWalletConnectLightningConnectionStringHandler(NostrClientPool nostrClientPool)
public NostrWalletConnectLightningConnectionStringHandler(NostrClientPool nostrClientPool,
Microsoft.Extensions.Logging.ILogger<NostrWalletConnectLightningClient> logger)
{
_nostrClientPool = nostrClientPool;
_logger = logger;
}
public ILightningClient? Create(string connectionString, Network network, out string? error)
{
Expand All @@ -41,6 +44,6 @@ public NostrWalletConnectLightningConnectionStringHandler(NostrClientPool nostrC
}

error = null;
return new NostrWalletConnectLightningClient(_nostrClientPool, uri, network);
return new NostrWalletConnectLightningClient(_nostrClientPool, uri, network, _logger);
}
}
Loading