fix: support ExtensionSigner NIP-44 API pattern

Problem:
Browser extension signers (nos2x, Alby, etc.) were failing with:
"Signer does not support NIP-44 decryption"

Root Cause:
ExtensionSigner exposes NIP-44 as signer.nip44.encrypt/decrypt
Other signers expose it as signer.nip44Encrypt/nip44Decrypt

Solution:
Created helper functions that try both patterns:
1. Direct methods (PasswordSigner, NostrConnectSigner)
2. nip44 getter methods (ExtensionSigner)

Files Changed:
- src/services/gift-wrap.ts: nip44Decrypt helper in decryptGiftWrap
- src/lib/chat/adapters/nip-17-adapter.ts: nip44Encrypt helper in sendMessage

This enables NIP-17 DMs to work with all signer types.
This commit is contained in:
Claude
2026-01-20 09:58:52 +00:00
parent a4f302f0d6
commit a7e45d346e
2 changed files with 40 additions and 18 deletions

View File

@@ -168,10 +168,26 @@ export class Nip17Adapter extends ChatProtocolAdapter {
throw new Error("No active account or signer");
}
// Check if signer supports NIP-44 encryption
if (!activeSigner.nip44Encrypt) {
// Helper to call NIP-44 encrypt (supports both signer patterns)
const nip44Encrypt = async (
pubkey: string,
plaintext: string,
): Promise<string> => {
// Try direct method (PasswordSigner, NostrConnectSigner, etc.)
if (typeof activeSigner.nip44Encrypt === "function") {
return await activeSigner.nip44Encrypt(pubkey, plaintext);
}
// Try nip44 getter (ExtensionSigner)
if (
activeSigner.nip44 &&
typeof activeSigner.nip44.encrypt === "function"
) {
return await activeSigner.nip44.encrypt(pubkey, plaintext);
}
throw new Error("Signer does not support NIP-44 encryption");
}
};
const recipientPubkey = conversation.participants.find(
(p) => p.pubkey !== activePubkey,
@@ -230,10 +246,7 @@ export class Nip17Adapter extends ChatProtocolAdapter {
// Step 2: Create the seal (kind 13)
// Encrypt the rumor with conversation key (sender → recipient)
const rumorJSON = JSON.stringify(rumor);
const encryptedRumor = await activeSigner.nip44Encrypt(
recipientPubkey,
rumorJSON,
);
const encryptedRumor = await nip44Encrypt(recipientPubkey, rumorJSON);
// Sign the seal
const sealDraft = {

View File

@@ -568,17 +568,30 @@ class GiftWrapManager {
throw new Error("Gift wrap not addressed to this pubkey");
}
// Helper to call NIP-44 decrypt (supports both signer patterns)
const nip44Decrypt = async (
pubkey: string,
ciphertext: string,
): Promise<string> => {
// Try direct method (PasswordSigner, NostrConnectSigner, etc.)
if (typeof signer.nip44Decrypt === "function") {
return await signer.nip44Decrypt(pubkey, ciphertext);
}
// Try nip44 getter (ExtensionSigner)
if (signer.nip44 && typeof signer.nip44.decrypt === "function") {
return await signer.nip44.decrypt(pubkey, ciphertext);
}
throw new Error("Signer does not support NIP-44 decryption");
};
// Step 1: Decrypt the gift wrap to get the seal (kind 13)
// The gift wrap is encrypted with the conversation key between
// the random ephemeral key (giftWrap.pubkey) and our key (recipientPubkey)
let sealJSON: string;
try {
// Check if signer has nip44Decrypt capability
if (!signer.nip44Decrypt) {
throw new Error("Signer does not support NIP-44 decryption");
}
sealJSON = await signer.nip44Decrypt(giftWrap.pubkey, giftWrap.content);
sealJSON = await nip44Decrypt(giftWrap.pubkey, giftWrap.content);
} catch (error) {
throw new Error(`Failed to decrypt gift wrap: ${error}`);
}
@@ -601,11 +614,7 @@ class GiftWrapManager {
// the sender (seal.pubkey) and us (recipientPubkey)
let rumorJSON: string;
try {
if (!signer.nip44Decrypt) {
throw new Error("Signer does not support NIP-44 decryption");
}
rumorJSON = await signer.nip44Decrypt(seal.pubkey, seal.content);
rumorJSON = await nip44Decrypt(seal.pubkey, seal.content);
} catch (error) {
throw new Error(`Failed to decrypt seal: ${error}`);
}