Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
11af3da
feat: deligate tasks to isolates
1-leo Dec 3, 2025
2b1d249
fix: websocket state check optimization
1-leo Dec 4, 2025
f368849
refactor: decode nip01 event in isolate
1-leo Dec 4, 2025
36ec5fe
refactor: nip01 immutable
1-leo Dec 5, 2025
8811870
refactor: event service
1-leo Dec 5, 2025
b7a7d40
refactor: signer, amber
1-leo Dec 5, 2025
765ec56
refactor: entities
1-leo Dec 5, 2025
225f59f
refactor usecases
1-leo Dec 5, 2025
c9dfc97
refactor: other packages
1-leo Dec 5, 2025
bf19d54
refactor: valid sig
1-leo Dec 5, 2025
7c50a16
refactor: ndk tests
1-leo Dec 5, 2025
240faf8
refactor: immutable event sources
1-leo Dec 6, 2025
37866c6
refactor(fix): sign event in test
1-leo Dec 6, 2025
59f750f
refactor(fix): auth event id
1-leo Dec 6, 2025
0413531
refactor: zap request calc id
1-leo Dec 6, 2025
35d0cd8
refactor(fix): mock relay fix send encode
1-leo Dec 6, 2025
9ea161b
refactor(fix): brodcast detect need for signing
1-leo Dec 6, 2025
338e975
refactor(fix): use signed event in sets engine
1-leo Dec 6, 2025
6f66a4e
refactor(fix): encode gift wrap
1-leo Dec 6, 2025
e8c1933
refactor(fix): encode zapRequest
1-leo Dec 6, 2025
2fb7dd0
fix: add copy with to extended class
1-leo Dec 6, 2025
8b47398
refactor: nip07, amber, sembast
1-leo Dec 6, 2025
6a1d852
refactor: proof of work as instance
1-leo Dec 6, 2025
9b57773
fix: blossom signing
1-leo Dec 12, 2025
052b634
chore(fix) missing sign return value
1-leo Dec 13, 2025
2b469e9
nip-01 immutable light
frnandu Dec 22, 2025
c65eaaa
Merge pull request #348 from relaystr/fix/nip-01-immutable-light
frnandu Dec 22, 2025
72c2173
feat: isolate pool
1-leo Dec 24, 2025
1aa82c5
chore: set isolate pool size to 1
1-leo Dec 24, 2025
6fc4209
feat: eose extract request id
1-leo Dec 24, 2025
22f7e13
feat: preserve order decoding
1-leo Dec 24, 2025
190303d
Merge pull request #332 from relaystr/fix-websocket-performance-issue…
1-leo Jan 14, 2026
3fdfe0e
Merge branch 'master' into fix-websocket-performance-issues-2
1-leo Jan 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import 'dart:convert';

import 'package:ndk/ndk.dart';
import 'package:ndk/shared/nips/nip19/nip19.dart';

import '../../data_sources/amber_flutter.dart';

Expand All @@ -18,13 +17,15 @@ class AmberEventSigner implements EventSigner {
});

@override
Future<void> sign(Nip01Event event) async {
Future<Nip01Event> sign(Nip01Event event) async {
final npub = publicKey.startsWith('npub')
? publicKey
: Nip19.encodePubKey(publicKey);
Map<dynamic, dynamic> map = await amberFlutterDS.amber.signEvent(
currentUser: npub, eventJson: jsonEncode(event.toJson()), id: event.id);
event.sig = map['signature'];
currentUser: npub,
eventJson: Nip01EventModel.fromEntity(event).toJsonString(),
id: event.id);
return event.copyWith(sig: map['signature']);
}

@override
Expand Down
7 changes: 2 additions & 5 deletions packages/isar/lib/data_layer/models/db/db_event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class DbEvent extends Nip01Event {
String get content => super.content;

@override
String get sig => super.sig;
String? get sig => super.sig;

@override
bool? get validSig => super.validSig;
Expand All @@ -56,12 +56,9 @@ class DbEvent extends Nip01Event {
required super.tags,
required super.content,
super.createdAt,
required String sig,
String? sig,
bool? validSig,
required List<String> sources}) {
super.sig = sig;
super.validSig = validSig;
super.sources = sources;
}

static DbEvent fromNip01Event(Nip01Event event) {
Expand Down
2 changes: 1 addition & 1 deletion packages/isar/lib/data_layer/models/db/db_event.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions packages/ndk/example/bunkers_example.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ Future<void> main() async {
tags: [],
);

await ndk.accounts.sign(event);
final signedEvent = await ndk.accounts.sign(event);
log('Event signed successfully!');
log('Event ID: ${event.id}');
log('Event ID: ${signedEvent.id}');
} catch (e) {
log('Error: $e');
}
Expand Down
19 changes: 14 additions & 5 deletions packages/ndk/example/nip13_pow_example.dart
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
import 'dart:developer';

import 'package:ndk/domain_layer/entities/nip_01_event.dart';
import 'package:ndk/ndk.dart';
import 'package:ndk/shared/nips/nip01/bip340.dart';

void main() async {
final keyPair = Bip340.generatePrivateKey();

final minedEvent = Nip01Event(
final event = Nip01Event(
pubKey: keyPair.publicKey,
kind: 1,
tags: [],
content: 'message',
).minePoW(12);
);

log(minedEvent.id); // the id will start with "000"
/// your global NDK instance
final ndk = Ndk.emptyBootstrapRelaysConfig();

if (minedEvent.checkPoWDifficulty(10)) {
/// pass your event to the proof of work usecase
final minedEvent =
await ndk.proofOfWork.minePoW(event: event, targetDifficulty: 10);

/// the id will start with "000"
log(minedEvent.id);

/// check the difficulty
if (ndk.proofOfWork.checkPoWDifficulty(event: event, targetDifficulty: 10)) {
log('Event has difficulty >= 10');
}
}
4 changes: 2 additions & 2 deletions packages/ndk/example/nostrconnect_example.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ Future<void> main() async {
tags: [],
);

await ndk.accounts.sign(event);
final signedEvent = await ndk.accounts.sign(event);
log('Event signed successfully!');
log('Event ID: ${event.id}');
log('Event ID: ${signedEvent.id}');
} catch (e) {
log('Error: $e');
}
Expand Down
14 changes: 6 additions & 8 deletions packages/ndk/lib/data_layer/data_sources/websocket_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ class WebsocketDSClient {
}

bool isOpen() {
return ws.connection.state == Connected() ||
ws.connection.state == Reconnected();
final state = ws.connection.state;
return state is Connected || state is Reconnected;
}

bool isConnecting() {
Expand All @@ -36,15 +36,13 @@ class WebsocketDSClient {
}

int? closeCode() {
return ws.connection.state == Disconnected()
? (ws.connection.state as Disconnected).code
: null;
final state = ws.connection.state;
return state is Disconnected ? state.code : null;
}

String? closeReason() {
return ws.connection.state == Disconnected()
? (ws.connection.state as Disconnected).reason
: null;
final state = ws.connection.state;
return state is Disconnected ? state.reason : null;
}
}
// coverage:ignore-end
181 changes: 181 additions & 0 deletions packages/ndk/lib/data_layer/models/nip_01_event_model.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
import 'dart:convert';

import '../../domain_layer/entities/nip_01_event.dart';
import '../../shared/helpers/list_casting.dart';
import '../../shared/nips/nip19/nip19.dart';

/// Data model for NIP-01 Event
/// Extends [Nip01Event] entity with serialization methods to/from JSON and other formats
class Nip01EventModel extends Nip01Event {
/// creates a new [Nip01EventModel] instance
Nip01EventModel({
required super.id,
required super.pubKey,
required super.createdAt,
required super.kind,
required super.tags,
required super.content,
required super.sig,
super.validSig,
super.sources = const [],
});

/// creates a copy of this event with the given fields replaced by the new values \
/// needed so other packages depending on the extened nip01_event.dart can use copyWith
@override
Nip01EventModel copyWith({
String? id,
String? pubKey,
int? createdAt,
int? kind,
List<List<String>>? tags,
String? content,
String? sig,
bool? validSig,
List<String>? sources,
}) {
return Nip01EventModel(
id: id ?? this.id,
pubKey: pubKey ?? this.pubKey,
createdAt: createdAt ?? this.createdAt,
kind: kind ?? this.kind,
tags: tags ?? this.tags,
content: content ?? this.content,
sig: sig ?? this.sig,
validSig: validSig ?? this.validSig,
sources: sources ?? this.sources);
}

/**
* encoding/decoding methods
*/

/// creates a new [Nip01EventModel] instance from a [Nip01Event] entity
factory Nip01EventModel.fromEntity(Nip01Event event) {
return Nip01EventModel(
id: event.id,
pubKey: event.pubKey,
createdAt: event.createdAt,
kind: event.kind,
tags: event.tags,
content: event.content,
sig: event.sig,
sources: event.sources,
validSig: event.validSig,
);
}

/// creates a new [Nip01EventModel] instance from a JSON object
factory Nip01EventModel.fromJson(Map<dynamic, dynamic> data) {
final id = data['id'] as String? ?? '';
final pubKey = data['pubkey'] as String? ?? '';
final createdAt = data['created_at'] as int;
final kind = data['kind'] as int;
final tags = castToListOfListOfString(data['tags']);
final content = data['content'] as String? ?? '';

/// '' to support rumor events
final sig = (data['sig'] as String?);

return Nip01EventModel(
id: id,
pubKey: pubKey,
createdAt: createdAt,
kind: kind,
tags: tags,
content: content,
sig: sig,
);
}

/// Returns the Event object as a JSON object
Map<String, dynamic> toJson() {
return {
'id': id,
'pubkey': pubKey,
'created_at': createdAt,
'kind': kind,
'tags': tags,
'content': content,
'sig': sig
};
}

/// Returns the Event object as a JSON string
String toJsonString() {
final jsonMap = toJson();
return json.encode(jsonMap);
}

/// Encode this event as a nevent (NIP-19 event reference)
///
/// Returns a bech32-encoded nevent string that includes:
/// - Event ID (required)
/// - Author pubkey (included)
/// - Kind (included)
/// - Relay hints from event.sources (if available)
///
/// Usage: `final nevent = event.nevent;`
String get nevent {
return Nip19.encodeNevent(
eventId: id,
author: pubKey,
kind: kind,
relays: sources.isEmpty ? null : sources,
);
}

/// Encode this event as an naddr (NIP-19 addressable event coordinate)
///
/// Only works for addressable/replaceable events (kind >= 10000 or kind 0, 3, 41)
/// Requires a "d" tag to identify the event.
///
/// Returns a bech32-encoded naddr string or null if:
/// - Event is not addressable/replaceable
/// - Event doesn't have a "d" tag
///
/// Usage: `final naddr = event.naddr;`
String? get naddr {
// Check if this is an addressable event
if (!_isAddressableKind(kind)) {
return null;
}

// Get the "d" tag identifier
final identifier = getDtag();
if (identifier == null) {
return null;
}

return Nip19.encodeNaddr(
identifier: identifier,
pubkey: pubKey,
kind: kind,
relays: sources.isEmpty ? null : sources,
);
}

/// Returns the Event object as a base64-encoded JSON string
String toBase64() {
return base64Encode(utf8.encode(json.encode(toJson())));
}

/// Check if an event kind is addressable/replaceable
///
/// According to NIP-01:
/// - Replaceable events: 0, 3, 41
/// - Parameterized replaceable events: 10000-19999, 30000-39999
bool _isAddressableKind(int kind) {
// Replaceable events
if (kind == 0 || kind == 3 || kind == 41) {
return true;
}

// Parameterized replaceable events
if ((kind >= 10000 && kind <= 19999) || (kind >= 30000 && kind <= 39999)) {
return true;
}

return false;
}
}
Loading
Loading