NIP-59 (Gift Wrap) Implementation
The Nip59
class provides utilities for implementing the Gift Wrap protocol (NIP-59), allowing secure event wrapping and unwrapping. This implementation works with any signer that supports encryption, making it versatile for different authentication methods.
Key Features
- Event wrapping (encryption) for specific recipients
- Event unwrapping (decryption) of received wrapped events
- Automatic ephemeral wrapper generation
- Caching of previously unwrapped events
- Compatible with all signer implementations
Basic Usage
typescript
import { Nip59 } from '@welshman/signer'
import { createEvent, DIRECT_MESSAGE } from '@welshman/util'
// Create a NIP-59 instance from any signer
const nip59 = Nip59.fromSigner(mySigner)
// Wrap an event
const rumor = await nip59.wrap(
recipientPubkey,
createEvent(DIRECT_MESSAGE, {
content: "Secret message",
tags: [["p", recipientPubkey]]
})
)
// The wrapped event to publish
const wrappedEvent = rumor.wrap
// Unwrap a received event
const unwrapped = await nip59.unwrap(receivedWrappedEvent)
Wrapping Process
The wrapping process involves multiple steps:
- Create the rumor (original event)
- Create the seal (encrypted rumor)
- Create the wrap (encrypted seal)
typescript
export const wrap = async (
signer: ISigner,
wrapper: ISigner,
pubkey: string,
template: StampedEvent,
tags: string[][] = []
) => {
const rumor = await getRumor(signer, template)
const seal = await getSeal(signer, pubkey, rumor)
const wrap = await getWrap(wrapper, pubkey, seal, tags)
return Object.assign(rumor, {wrap})
}
API Reference
Constructor & Factory Methods
typescript
class Nip59 {
// Constructor
constructor(signer: ISigner, wrapper?: ISigner)
// Factory Methods
static fromSigner(signer: ISigner): Nip59
static fromSecret(secret: string): Nip59
// Instance Methods
/**
* Wraps an event for a specific recipient
* @param pubkey Recipient's public key
* @param template The event to wrap
* @param tags Additional tags for the wrap event (optional)
* @returns Promise<UnwrappedEvent> Original event and its wrapped version
*/
wrap(
pubkey: string,
template: StampedEvent,
tags?: string[][]
): Promise<UnwrappedEvent>
/**
* Unwraps a received wrapped event
* @param event The wrapped event to decrypt
* @returns Promise<UnwrappedEvent> The original unwrapped event
*/
unwrap(event: SignedEvent): Promise<UnwrappedEvent>
/**
* Creates a new instance with a specific wrapper signer
* @param wrapper Signer to use for wrapping events
* @returns Nip59 New instance with the specified wrapper
*/
withWrapper(wrapper: ISigner): Nip59
}
Detailed Examples
Basic Wrapping & Unwrapping
typescript
import { Nip59, Nip01Signer } from '@welshman/signer'
import { createEvent, DIRECT_MESSAGE } from '@welshman/util'
async function example() {
// Create NIP-59 instance
const signer = new Nip01Signer(mySecret)
const nip59 = Nip59.fromSigner(signer)
// Create and wrap an event
const event = createEvent(DIRECT_MESSAGE, {
content: "Secret message",
tags: [["p", recipientPubkey]]
})
const rumor = await nip59.wrap(recipientPubkey, event)
// rumor contains:
// - The original event (rumor)
// - The wrapped version to publish (rumor.wrap)
// Later, unwrap a received event
const unwrapped = await nip59.unwrap(receivedEvent)
}
Custom Wrapper Signer
typescript
import { Nip59, Nip01Signer } from '@welshman/signer'
// Create with specific wrapper
const nip59 = new Nip59(
mainSigner,
Nip01Signer.ephemeral() // Custom wrapper
)
// Or add wrapper to existing instance
const nip59WithWrapper = nip59.withWrapper(
Nip01Signer.ephemeral()
)