UNPKG

@nostr-dev-kit/ndk

Version:

NDK - Nostr Development Kit

125 lines (113 loc) 4.26 kB
import { getEventHash } from "nostr-tools"; import type { NDKSigner } from "../signers/index.js"; import { NDKPrivateKeySigner } from "../signers/private-key"; import type { NDKEncryptionScheme } from "../types.js"; import { NDKUser } from "../user/index.js"; import { NDKEvent, type NDKRawEvent, type NostrEvent } from "./index.js"; import { NDKKind } from "./kinds/index.js"; export type GiftWrapParams = { scheme?: NDKEncryptionScheme; rumorKind?: number; wrapTags?: string[][]; }; /** * Instantiate a new (Nip59 gift wrapped) NDKEvent from any NDKEvent * @param event * @param recipient * @param signer * @param params * @returns */ export async function giftWrap( event: NDKEvent, recipient: NDKUser, signer?: NDKSigner, params: GiftWrapParams = {} ): Promise<NDKEvent> { let _signer = signer; params.scheme ??= "nip44"; if (!_signer) { if (!event.ndk) throw new Error("no signer available for giftWrap"); _signer = event.ndk.signer; } if (!_signer) throw new Error("no signer"); if (!_signer.encryptionEnabled || !_signer.encryptionEnabled(params.scheme)) throw new Error("signer is not able to giftWrap"); const rumor = getRumorEvent(event, params?.rumorKind); const seal = await getSealEvent(rumor, recipient, _signer, params.scheme); const wrap = await getWrapEvent(seal, recipient, params); return new NDKEvent(event.ndk, wrap); } /** * Instantiate a new (Nip59 un-wrapped rumor) NDKEvent from any gift wrapped NDKEvent * @param event */ export async function giftUnwrap( event: NDKEvent, sender?: NDKUser, signer?: NDKSigner, scheme: NDKEncryptionScheme = "nip44" ): Promise<NDKEvent> { const _sender = sender || new NDKUser({ pubkey: event.pubkey }); let _signer = signer; if (!_signer) { if (!event.ndk) throw new Error("no signer available for giftUnwrap"); _signer = event.ndk.signer; } if (!signer) throw new Error("no signer"); try { const seal = JSON.parse(await signer.decrypt(_sender, event.content, scheme)) as NostrEvent; if (!seal) throw new Error("Failed to decrypt wrapper"); if (!new NDKEvent(undefined, seal).verifySignature(false)) throw new Error("GiftSeal signature verification failed!"); const rumorSender = new NDKUser({ pubkey: seal.pubkey }); const rumor = JSON.parse(await signer.decrypt(rumorSender, seal.content, scheme)); if (!rumor) throw new Error("Failed to decrypt seal"); if (rumor.pubkey !== seal.pubkey) throw new Error("Invalid GiftWrap, sender validation failed!"); return new NDKEvent(event.ndk, rumor as NostrEvent); } catch (_e) { return Promise.reject("Got error unwrapping event! See console log."); } } function getRumorEvent(event: NDKEvent, kind?: number): NDKEvent { const rumor = event.rawEvent() as Partial<NDKRawEvent>; rumor.kind = kind || rumor.kind || NDKKind.PrivateDirectMessage; rumor.sig = undefined; rumor.id = getEventHash(rumor as any); return new NDKEvent(event.ndk, rumor); } async function getSealEvent( rumor: NDKEvent, recipient: NDKUser, signer: NDKSigner, scheme: NDKEncryptionScheme = "nip44" ): Promise<NDKEvent> { const seal = new NDKEvent(rumor.ndk); seal.kind = NDKKind.GiftWrapSeal; seal.created_at = approximateNow(5); seal.content = JSON.stringify(rumor.rawEvent()); await seal.encrypt(recipient, signer, scheme); await seal.sign(signer); return seal; } async function getWrapEvent( sealed: NDKEvent, recipient: NDKUser, params?: GiftWrapParams, scheme: NDKEncryptionScheme = "nip44" ): Promise<NDKEvent> { const signer = NDKPrivateKeySigner.generate(); const wrap = new NDKEvent(sealed.ndk); wrap.kind = NDKKind.GiftWrap; wrap.created_at = approximateNow(5); if (params?.wrapTags) wrap.tags = params.wrapTags; wrap.tag(recipient); wrap.content = JSON.stringify(sealed.rawEvent()); await wrap.encrypt(recipient, signer, scheme); await wrap.sign(signer); return wrap; } function approximateNow(drift = 0) { return Math.round(Date.now() / 1000 - Math.random() * 10 ** drift); }