Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
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,20 +26,18 @@ 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;
}

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