nostr_tools package logo

A Dart package that makes it easy to work with the nostr protocol and develop nostr clients.

Usage

To use this package, add nostr_tools as a dependency in your pubspec.yaml file.

Generating a private key and a public key

import 'package:nostr_tools/nostr_tools.dart';

void main() {
  final keyGenerator = KeyApi();

  final privateKey = keyGenerator.generatePrivateKey();
  print('[+] privateKey: $privateKey');
  // [+] privateKey: b2352adb186508e7f617105a6dc070df531f53b56cf8744816fdb838891dc9b7

  final publicKey = keyGenerator.getPublicKey(privateKey);
  print('[+] publicKey: $publicKey');
  // [+] publicKey: 9d088c4377b9866fad945d949de8f626784b1639f93bf090c1cea6727f17dd51
}

Creating, signing and verifying events

import 'package:nostr_tools/nostr_tools.dart';

void main() {
  final keyApi = KeyApi();
  final eventApi = EventApi();

  final privateKey = keyApi.generatePrivateKey();
  final publicKey = keyApi.getPublicKey(privateKey);

  final event = Event(
    kind: 1,
    tags: [],
    content: 'content',
    created_at: DateTime.now().millisecondsSinceEpoch ~/ 1000,
    pubkey: publicKey,
  );

  event.id = eventApi.getEventHash(event);
  event.sig = eventApi.signEvent(event, privateKey);

  if (eventApi.verifySignature(event)) print('[+] sig is valid');
}

Interacting with a relay

void main() async {
  final relay = RelayApi(relayUrl: 'wss://relay.damus.io');

  final stream = await relay.connect();

  relay.on((event) {
    if (event == RelayEvent.connect) {
      print('[+] connected to ${relay.relayUrl}');
    } else if (event == RelayEvent.error) {
      print('[!] failed to connect to ${relay.relayUrl}');
    }
  });

  relay.sub([
    Filter(
      kinds: [1],
      limit: 10,
      since: DateTime.now().millisecondsSinceEpoch ~/ 1000,
    )
  ]);

  stream.listen((Message message) {
    if (message.type == 'EVENT') {
      Event event = message.message;
      print('[+] Received event: ${event.content}');
    } else if (message.type == 'OK') {
      print('[+] Event Published: ${message.message}');
    }
  });

  // let's publish a new event while simultaneously monitoring the relay for it
  final privateKey =
      'b2352adb186508e7f617105a6dc070df531f53b56cf8744816fdb838891dc9b7';

  final event = EventApi().finishEvent(
    Event(
      kind: 1,
      tags: [],
      content: 'hello world',
      created_at: DateTime.now().millisecondsSinceEpoch ~/ 1000,
    ),
    privateKey,
  );

  relay.publish(event);
}

Interacting with multiple relays

void main() async {
  final relaysList = [
    'wss://relay.damus.io',
    'wss://relay.nostr.info',
    'wss://eden.nostr.land',
    'wss://nostr-pub.wellorder.net',
    'wss://nos.lol'
  ];
  final relayPool = RelayPoolApi(relaysList: relaysList);

  final stream = await relayPool.connect();

  relayPool.on((event) {
    if (event == RelayEvent.connect) {
      print('[+] connected to: ${relayPool.connectedRelays}');
    } else if (event == RelayEvent.error) {
      print('[!] failed to connect to: ${relayPool.failedRelays}');
    }
  });

  relayPool.sub([
    Filter(
      kinds: [1],
      limit: 10,
      since: DateTime.now().millisecondsSinceEpoch ~/ 1000,
    )
  ]);

  stream.listen((Message message) {
    if (message.type == 'EVENT') {
      Event event = message.message;
      print('[+] Received event: ${event.content}');
    }
  });

  final privateKey =
      'ccbe92bd853e3661bace63df5b8338dcbaa2c766e2dbb0b90d45b2dd58efaae4';

  final event = EventApi().finishEvent(
    Event(
      kind: 1,
      tags: [],
      content: 'hello world',
      created_at: DateTime.now().millisecondsSinceEpoch ~/ 1000,
    ),
    privateKey,
  );

  relayPool.publish(event);
}

Querying profile data from a NIP-05 address

void main() async {
  var nip05 = Nip05();
  var profile = await nip05.queryProfile('anipy@aniketambore.github.io');
  print('[+] Pubkey: ${profile?.pubkey}');
  // [+] Pubkey: c7c187ba76532d55723490af88e76dd570fe551a2461ca9ab542b503bbb25045

  print('[+] Relays: ${profile?.relays}');
  // [+] Relays: [wss://relay.damus.io, wss://nos.lol, wss://relay.snort.social]
}

Encoding and decoding NIP-19 codes

void main() {
  final keyGenerator = KeyApi();
  final nip19 = Nip19();

  final sk = keyGenerator.generatePrivateKey();
  final nsec = nip19.nsecEncode(sk);
  final nsecDecoded = nip19.decode(nsec);
  assert(nsecDecoded['type'] == 'nsec');
  assert(nsecDecoded['data'] == sk);

  final pk = keyGenerator.getPublicKey(sk);
  final npub = nip19.npubEncode(pk);
  final npubDecoded = nip19.decode(npub);
  assert(npubDecoded['type'] == 'npub');
  assert(npubDecoded['data'] == pk);

  final relays = [
    'wss://relay.nostr.example.mydomain.example.com',
    'wss://nostr.banana.com'
  ];
  final nprofile =
      nip19.nprofileEncode(ProfilePointer(pubkey: pk, relays: relays));
  final nprofileDecode = nip19.decode(nprofile);
  assert(nprofileDecode['type'] == 'nprofile');
  assert(nprofileDecode['data']['pubkey'] == pk);
  assert(nprofileDecode['data']['relays'].length == 2);
}

Encrypting and decrypting direct messages

void main() {
  final keyGenerator = KeyApi();
  final nip04 = Nip04();

  final aliceSK = keyGenerator.generatePrivateKey();
  final alicePK = keyGenerator.getPublicKey(aliceSK);
  final bobSK = keyGenerator.generatePrivateKey();
  final bobPK = keyGenerator.getPublicKey(bobSK);

  var aliceMessageToBob = 'Hello Bob!';
  var cipherText = nip04.encrypt(aliceSK, bobPK, aliceMessageToBob);
  print('[+] cipherText: $cipherText');

  var bobDecodingMessage = nip04.decrypt(bobSK, alicePK, cipherText);
  print('[+] plainText: $bobDecodingMessage');
}

Take a look at the example project for developing a simple basic nostr client in flutter.

Reference

Libraries

nostr_tools