Lists

high level

Example

final myset = await ndk.lists.getSetByName(
  name: "myset",
  kind: Nip51List.kRelaySet,
  customSigner: mySigner,
);

if (myset == null) {
  print("set not found");
  return;
}
print("received a set with ${myset.elements.length} elements");

How to use

We distinguish between lists and sets:

  • Lists: Single lists identified by kind (e.g., bookmarks, mute list)
  • Sets: Named collections identified by kind + name/d-tag (e.g., relay sets, follow sets)

Both can have public and private (encrypted) elements.

Current behavior

For public list/set content:

  • reads use the latest visible event for the relevant list or set
  • cached results are reused on later reads

For private list/set content:

  • the original event content stays encrypted
  • decrypted private tags can be loaded through NDK's decrypted payload cache
  • repeated reads can reuse cached plaintext instead of decrypting again

Lists Methods

getSingleNip51List

Retrieves a NIP-51 list by kind.

  return _decryptedEventPayloads.loadOrDecrypt(
    event: event,
    viewerPubKey: signer.getPublicKey(),
    scheme: isNip04
        ? DecryptedPayloadScheme.nip04
        : DecryptedPayloadScheme.nip44,
    decrypt: () => isNip04
        // ignore: deprecated_member_use_from_same_package
        ? signer.decrypt(event.content, signer.getPublicKey())
        : signer.decryptNip44(
            ciphertext: event.content,
            senderPubKey: signer.getPublicKey(),
          ),
  );
}

Future<void> _applyPrivateTags({

addElementToList

Adds an element to a list. Creates the list if it doesn't exist.

  return list;
}

Future<Nip51Set?> _parseSetEvent(
  Nip01Event event,
  EventSigner? signer,
) async {
  final set = await Nip51Set.fromEvent(event, null);
  if (set != null && signer != null) {
    await _applyPrivateTags(event: event, signer: signer, list: set);
  }
  return set;
}

///* lists *///

Future<Nip51List?> _getCachedNip51List(int kind, EventSigner signer) async {
  List<Nip01Event>? events = await _cacheManager.loadEvents(
    pubKeys: [signer.getPublicKey()],
    kinds: [kind],

removeElementFromList

Removes an element from a list.

            filters: [
              Filter(authors: [signer.getPublicKey()], kinds: [kind]),
            ],
            timeout: timeout,
          )
          .stream) {
    if (refreshedList == null ||
        refreshedList.createdAt <= event.createdAt) {
      refreshedList = await _parseListEvent(event, signer);
      // if (Helpers.isNotBlank(event.content)) {
      //   Nip51List? decryptedList = await Nip51List.fromEvent(event, signer);
      //   refreshedList = decryptedList;
      // }
      await _cacheManager.saveEvent(event);
    }
  }
  return refreshedList;
}

Sets Methods

getSetByName

Gets a specific set by name (d-tag) and kind.

}

/// Removes an element from a NIP-51 list.
///
/// Updates the list by removing the specified element, then broadcasts
/// the updated list to relays and updates the cache.
///
/// [kind] the kind of NIP-51 list \
/// [tag] the tag type of the element to remove \
/// [value] the value to remove from the list \
/// [broadcastRelays] optional specific relays to broadcast to
///
/// Returns the updated list, or null if the list doesn't exist.\
/// Throws an exception if no event signer is available.
Future<Nip51List?> removeElementFromList({
  required int kind,
  required String tag,
  required String value,

getPublicSets

Returns a stream of all public sets for a given public key and kind.

/// gets set by name with the specified signer
Future<Nip51Set?> _getCachedSetByName(
  String name,
  EventSigner signer,
  int kind,
) async {
  List<Nip01Event>? events = await _cacheManager.loadEvents(
    pubKeys: [signer.getPublicKey()],
    kinds: [kind],
  );
  events = events.where((event) {
    if (event.getDtag() != null && event.getDtag() == name) {
      return true;
    }
    return false;

addElementToSet

Adds an element to a named set. Creates the set if it doesn't exist.

      : null;
}

/// get a nip51 set
Stream<Iterable<Nip51Set>?> _getSets(
  int kind,
  EventSigner signer, {
  bool forceRefresh = false,
}) {
  final relaySets = <String, Nip51Set>{};

  return _requests
      .query(
        filters: [
          Filter(authors: [signer.getPublicKey()], kinds: [kind]),
        ],
        cacheRead: !forceRefresh,
      )
      .stream
      .where((event) => event.getDtag() != null)
      .asyncMap((event) async {

removeElementFromSet

Removes an element from a named set.

final EventSigner signer;

if (_eventSigner == null) {
  throw Exception("getSetByName() no account");
}
signer = _eventSigner!;

Nip51Set? relaySet = await _getCachedSetByName(name, signer, kind);
if (relaySet == null || forceRefresh) {
  Nip51Set? newRelaySet;
  await for (final event
      in _requests
          .query(
            filters: [
              Filter(
                authors: [signer.getPublicKey()],
                kinds: [kind],
                tags: {
                  "#d": [name],
                },
              ),
            ],

setCompleteSet

Overwrites or creates a complete set. Warning: This replaces the entire set.

    );
  }

  return _getSets(kind, mySigner, forceRefresh: forceRefresh);
}

/// Adds an element to a NIP-51 set.
///
/// If the set doesn't exist, it will be created. The updated set is
/// then broadcast to relays and cached locally.
///
/// [name] name of the set (d tag identifier) \
/// [tag] the tag type for the element (e.g., 'relay', 'p', 'e') \
/// [value] the value to add to the set \
/// [kind] kind of the set \

deleteSet

Deletes a set by name and broadcasts a deletion event.

final broadcastResponse = _broadcast.broadcast(
  nostrEvent: event,
  specificRelays: specificRelays,
  customSigner: _eventSigner,
);
await broadcastResponse.broadcastDoneFuture;

List<Nip01Event>? events = await _cacheManager.loadEvents(
  pubKeys: [_eventSigner!.getPublicKey()],
  kinds: [kind],
);
events = events.where((event) {
  if (event.getDtag() != null && event.getDtag() == name) {
    return true;
  }

Common Use Cases

Relay Sets

// Add relay to a set
await ndk.lists.addElementToSet(
  name: "my-relays",
  tag: "relay",
  value: "wss://relay.example.com",
  kind: Nip51List.kRelaySet,
);

// Get a relay set
final relaySet = await ndk.lists.getSetByName(
  name: "my-relays",
  kind: Nip51List.kRelaySet,
);

Bookmarks

// Add bookmark
await ndk.lists.addElementToList(
  kind: Nip51List.kBookmarks,
  tag: "e",
  value: eventId,
);

// Get bookmarks
final bookmarks = await ndk.lists.getSingleNip51List(
  Nip51List.kBookmarks,
  mySigner,
);

Follow Sets

// Add to follow set
await ndk.lists.addElementToSet(
  name: "close-friends",
  tag: "p",
  value: pubkey,
  kind: Nip51List.kFollowSet,
);

// Stream all public follow sets
ndk.lists.getPublicSets(
  kind: Nip51List.kFollowSet,
  publicKey: somePubkey,
).listen((sets) {
  print("Found ${sets?.length ?? 0} follow sets");
});