NDKNutzapMonitor
The NDKNutzapMonitor class monitors a user's nutzap inbox for new nutzaps and processes them automatically. It handles the full lifecycle of nutzaps, from discovery to redemption.
Features
- Monitors relays for nutzaps sent to a specific user
- Automatically redeems nutzaps when the appropriate private key is available
- Keeps track of nutzap states (initial, processing, redeemed, spent, error)
- Persists states across application sessions using the configured NDK Cache Adapter
- Emits events for tracking monitor activity
Basic Usage
import { NDKNutzapMonitor } from "@nostr-dev-kit/ndk-wallet";
import NDK, { NDKUser, NDKPrivateKeySigner, NDKCashuMintList } from "@nostr-dev-kit/ndk";
import { NDKCashuWallet } from "@nostr-dev-kit/ndk-wallet"; // Example wallet
// Assume ndk: NDK, user: NDKUser, mintList?: NDKCashuMintList are initialized
// Assume myCashuWallet: NDKCashuWallet is initialized
// Assume myPrivateKeySigner: NDKPrivateKeySigner is initialized
// Create a monitor for a user
// The store is now automatically derived from ndk.cacheAdapter if available
const monitor = new NDKNutzapMonitor(ndk, user, {
mintList,
// No need to pass 'store' manually if using a compatible cache adapter
});
// Set the wallet to use for redeeming nutzaps
monitor.wallet = myCashuWallet;
// If you have extra private keys that might have been used to receive p2pk-locked keys, you can also add them
// this is almost always unnecessary -- the nutzap monitor will try to do this for you if the private keys are
// properly stored as nostr events per the NIP-60 spec.
await monitor.addPrivkey(myPrivateKeySigner);
// Start monitoring for nutzaps
await monitor.start({});
// Listen for events
monitor.on("redeemed", (events, amount) => {
console.log(`Redeemed ${events.length} nutzaps for ${amount} sats`);
});State Management
The monitor uses a state machine to track the status of each nutzap. The possible states (NdkNutzapStatus) are defined in @nostr-dev-kit/ndk:
INITIAL: First time we see a nutzapPROCESSING: Currently processing the nutzapREDEEMED: Successfully redeemedSPENT: The nutzap has already been spentMISSING_PRIVKEY: No private key available to redeem the nutzapTEMPORARY_ERROR: A transient error occurredPERMANENT_ERROR: A permanent error occurred (will not retry)INVALID_NUTZAP: The nutzap data is invalid
State Persistence (via Cache Adapter)
The NDKNutzapMonitor now leverages the configured ndk.cacheAdapter for persisting nutzap states across application sessions.
If the ndk.cacheAdapter implements the optional getAllNutzapStates and setNutzapState methods (as defined in the NDKCacheAdapter interface in @nostr-dev-kit/ndk), the monitor will automatically use these methods to load initial states and save updates.
Cache Adapter Interface Methods
The relevant methods in the NDKCacheAdapter interface are:
// Defined in @nostr-dev-kit/ndk
interface NDKCacheAdapter {
// ... other methods
/**
* Gets all nutzap states from the cache.
* @returns A map of event IDs to nutzap states.
*/
getAllNutzapStates?(): Promise<Map<NDKEventId, NDKNutzapState>>;
/**
* Sets the state of a nutzap in the cache.
* @param id The ID of the nutzap event.
* @param stateChange The partial state change to apply.
*/
setNutzapState?(id: NDKEventId, stateChange: Partial<NDKNutzapState>): Promise<void>;
}State Updates
When updating a nutzap's state, the monitor calls cacheAdapter.setNutzapState with only the changed properties (Partial<NDKNutzapState>), not the entire state object. This allows the cache adapter implementation to efficiently merge updates.
For example:
// Internal call within NDKNutzapMonitor
await ndk.cacheAdapter?.setNutzapState?.(nutzapId, {
status: NdkNutzapStatus.REDEEMED,
redeemedAmount: 100,
});Implementing Persistence
If you are creating a custom cache adapter and want it to support nutzap state persistence, you need to implement the getAllNutzapStates and setNutzapState methods.
NDK Mobile (@nostr-dev-kit/mobile) provides an implementation using SQLite via its NDKCacheAdapterSqlite. If you use this adapter with your NDK instance, nutzap state persistence will work automatically.
// Example: Initializing NDK with the SQLite adapter from ndk-mobile
import NDK from "@nostr-dev-kit/ndk";
import { NDKCacheAdapterSqlite } from "@nostr-dev-kit/mobile";
const cacheAdapter = new NDKCacheAdapterSqlite("my-ndk-cache.db");
await cacheAdapter.initialize(); // Important: Initialize the adapter
const ndk = new NDK({
cacheAdapter: cacheAdapter,
// ... other NDK options
});
// Pass the NDK instance to the adapter AFTER NDK is initialized
// This allows the adapter to use the NDK instance if needed (e.g., for deserializing events)
cacheAdapter.ndk = ndk;
// Now, when NDKNutzapMonitor is created with this ndk instance,
// it will use the SQLite adapter for persistence.Events
The monitor emits several events that you can listen for:
seen_in_unknown_mint: Emitted when a nutzap is seen in a mint not in the user's mint liststate_changed: Emitted when the state of a nutzap changesredeemed: Emitted when a nutzap is successfully redeemedseen: Emitted when a new nutzap is seenfailed: Emitted when a nutzap fails to be redeemed
Managing Private Keys
The monitor needs private keys to redeem nutzaps. Typically, you don't need to do this manually; setting the monitor.wallet property should provide the necessary keys if the wallet implementation supports it (like NDKCashuWallet).
You can also add extra private keys using:
await monitor.addPrivkey(privateKeySigner);You can add multiple private keys, and the monitor will automatically use the appropriate key for each nutzap.
If a nutzap requires a private key that isn't available, it will be marked as MISSING_PRIVKEY. If the key is later added, the monitor will automatically attempt to redeem the nutzap.
Error Handling
The monitor handles various error conditions:
- If a nutzap cannot be redeemed due to a missing private key, it's marked as
MISSING_PRIVKEY - If the redemption fails with a transient error, it's marked as
TEMPORARY_ERROR - If the redemption fails with a permanent error (e.g., "unknown public key size"), it's marked as
PERMANENT_ERROR
The monitor will automatically retry nutzaps with temporary errors but not those with permanent errors.