diff --git a/packages/ndk/lib/data_layer/repositories/wallets/wallets_repo_impl.dart b/packages/ndk/lib/data_layer/repositories/wallets/wallets_repo_impl.dart index 70f2d6195..793ffd69b 100644 --- a/packages/ndk/lib/data_layer/repositories/wallets/wallets_repo_impl.dart +++ b/packages/ndk/lib/data_layer/repositories/wallets/wallets_repo_impl.dart @@ -1,3 +1,7 @@ +import 'dart:async'; + +import 'package:rxdart/rxdart.dart'; + import '../../../domain_layer/entities/wallet/wallet.dart'; import '../../../domain_layer/entities/wallet/wallet_balance.dart'; import '../../../domain_layer/entities/wallet/wallet_transaction.dart'; @@ -47,18 +51,34 @@ class WalletsRepoImpl implements WalletsRepo { } @override - Future removeWallet(String id) { + Future removeWallet(String id) async { + Wallet wallet = await getWallet(id); + if (wallet is NwcWallet) { + NwcWallet nwcWallet = wallet; + // close connection if exists + if (wallet.connection != null) { + await _nwcUseCase.disconnect(nwcWallet.connection!); + if (nwcWallet.balanceSubject != null) { + await nwcWallet.balanceSubject!.close(); + } + if (nwcWallet.transactionsSubject != null) { + await nwcWallet.transactionsSubject!.close(); + } + if (nwcWallet.pendingTransactionsSubject != null) { + await nwcWallet.pendingTransactionsSubject!.close(); + } + } + } return _cacheManger.removeWallet(id); } @override - Stream> getBalancesStream(String accountId) async* { + Stream> getBalancesStream(String id) async* { // delegate to appropriate use case based on account type - final useCase = await _getWalletUseCase(accountId); + final useCase = await _getWalletUseCase(id); if (useCase is Cashu) { // transform to WalletBalance - yield* useCase.balances.map((balances) => - balances.where((b) => b.mintUrl == accountId).expand((b) { + yield* useCase.balances.map((balances) => balances.where((b) => b.mintUrl == id).expand((b) { return b.balances.entries.map((entry) => WalletBalance( unit: entry.key, amount: entry.value, @@ -66,20 +86,57 @@ class WalletsRepoImpl implements WalletsRepo { )); }).toList()); } else if (useCase is Nwc) { - Wallet wallet = await getWallet(accountId); - final connection = await useCase.connect(wallet.metadata["nwcUrl"]); - final balanceResponse = await useCase.getBalance(connection); - yield [ - WalletBalance( - walletId: accountId, - unit: "sat", - amount: balanceResponse.balanceSats) - ]; + NwcWallet wallet = (await getWallet(id)) as NwcWallet; + if (!wallet.isConnected()) { + await _initNwcWalletConnection(wallet); + } + wallet.balanceSubject ??= BehaviorSubject>(); + + final balanceResponse = await useCase.getBalance(wallet.connection!); + wallet.balanceSubject!.add([WalletBalance(walletId: id, unit: "sat", amount: balanceResponse.balanceSats)]); + yield* wallet.balanceSubject!.stream; } else { throw UnimplementedError('Unknown account type for balances stream'); } } + Future _initNwcWalletConnection(NwcWallet wallet) async { + wallet.connection ??= await _nwcUseCase.connect(wallet.metadata["nwcUrl"], + doGetInfoMethod: true // TODO getInfo or not should be ndk config somehow + ); + + wallet.connection!.notificationStream.stream.listen((notification) async { + if (!notification.isPaymentReceived && !notification.isPaymentSent) { + return; // only incoming and outgoing payments are handled here + } + if (wallet.balanceSubject != null && notification.state == "settled") { + final balanceResponse = await _nwcUseCase.getBalance(wallet.connection!); + wallet.balanceSubject! + .add([WalletBalance(walletId: wallet.id, unit: "sat", amount: balanceResponse.balanceSats)]); + } + if (wallet.transactionsSubject != null || wallet.pendingTransactionsSubject != null) { + final transaction = NwcWalletTransaction( + id: notification.paymentHash, + walletId: wallet.id, + changeAmount: (notification.isIncoming ? notification.amount /1000 : -notification.amount /1000) as int, + unit: "sats", + walletType: WalletType.NWC, + state: notification.isSettled + ? WalletTransactionState.completed + : (notification.isPending?WalletTransactionState.pending: WalletTransactionState.failed), + metadata: notification.metadata ?? {}, + transactionDate: notification.settledAt ?? notification.createdAt, + initiatedDate: notification.createdAt, + ); + if (notification.isSettled) { + wallet.transactionsSubject!.add([transaction]); + } else if (notification.isPending) { + wallet.pendingTransactionsSubject!.add([transaction]); + } + } + }); + } + /// get notified about possible new wallets \ /// this is used to update the UI when new wallets are implicitly added \ /// like when receiving something on a not yet existing wallet @@ -101,46 +158,65 @@ class WalletsRepoImpl implements WalletsRepo { @override Stream> getPendingTransactionsStream( - String accountId, + String id, ) async* { - final useCase = await _getWalletUseCase(accountId); + final useCase = await _getWalletUseCase(id); if (useCase is Cashu) { /// filter transaction stream by id yield* useCase.pendingTransactions.map( - (transactions) => transactions - .where((transaction) => transaction.walletId == accountId) - .toList(), + (transactions) => transactions.where((transaction) => transaction.walletId == id).toList(), ); } else if (useCase is Nwc) { - // throw UnimplementedError( - // 'NWC pending transactions stream not implemented yet'); + NwcWallet wallet = (await getWallet(id)) as NwcWallet; + if (!wallet.isConnected()) { + await _initNwcWalletConnection(wallet); + } + wallet.pendingTransactionsSubject ??= BehaviorSubject>(); + final transactions = await _nwcUseCase.listTransactions(wallet.connection!, unpaid: true); + wallet.pendingTransactionsSubject!.add(transactions.transactions.reversed + .where((e) => e.state != null && e.state == "pending") + .map((e) => NwcWalletTransaction( + id: e.paymentHash, + walletId: wallet.id, + changeAmount: e.isIncoming ? e.amountSat : -e.amountSat, + unit: "sats", + walletType: WalletType.NWC, + state: e.state != null && e.state == "settled" + ? WalletTransactionState.completed + : WalletTransactionState.pending, + metadata: e.metadata ?? {}, + transactionDate: e.settledAt ?? e.createdAt, + initiatedDate: e.createdAt, + )) + .toList()); + yield* wallet.pendingTransactionsSubject!.stream; } else { - throw UnimplementedError( - 'Unknown account type for pending transactions stream'); + throw UnimplementedError('Unknown account type for pending transactions stream'); } } @override Stream> getRecentTransactionsStream( - String accountId, + String id, ) async* { - final useCase = await _getWalletUseCase(accountId); + final useCase = await _getWalletUseCase(id); if (useCase is Cashu) { /// filter transaction stream by id yield* useCase.latestTransactions.map( - (transactions) => transactions - .where((transaction) => transaction.walletId == accountId) - .toList(), + (transactions) => transactions.where((transaction) => transaction.walletId == id).toList(), ); } else if (useCase is Nwc) { - Wallet wallet = await getWallet(accountId); - final connection = await useCase.connect(wallet.metadata["nwcUrl"]); - final transactions = - await useCase.listTransactions(connection, unpaid: false); - yield transactions.transactions + NwcWallet wallet = (await getWallet(id)) as NwcWallet; + if (!wallet.isConnected()) { + await _initNwcWalletConnection(wallet); + } + wallet.transactionsSubject ??= BehaviorSubject>(); + final transactions = await _nwcUseCase.listTransactions(wallet.connection!, unpaid: false); + wallet.transactionsSubject!.add(transactions.transactions.reversed + .where((e) => e.state != null && e.state == "settled") .map((e) => NwcWalletTransaction( id: e.paymentHash, - walletId: accountId, + walletId: wallet.id, changeAmount: e.isIncoming ? e.amountSat : -e.amountSat, unit: "sats", walletType: WalletType.NWC, @@ -151,10 +227,10 @@ class WalletsRepoImpl implements WalletsRepo { transactionDate: e.settledAt ?? e.createdAt, initiatedDate: e.createdAt, )) - .toList(); + .toList()); + yield* wallet.transactionsSubject!.stream; } else { - throw UnimplementedError( - 'Unknown account type for recent transactions stream'); + throw UnimplementedError('Unknown account type for recent transactions stream'); } } @@ -175,8 +251,8 @@ class WalletsRepoImpl implements WalletsRepo { ); } - Future _getWalletUseCase(String accountId) async { - final account = await getWallet(accountId); + Future _getWalletUseCase(String id) async { + final account = await getWallet(id); switch (account.type) { case WalletType.CASHU: return _cashuUseCase; diff --git a/packages/ndk/lib/domain_layer/entities/wallet/wallet.dart b/packages/ndk/lib/domain_layer/entities/wallet/wallet.dart index ad0cf3d31..2707efb9c 100644 --- a/packages/ndk/lib/domain_layer/entities/wallet/wallet.dart +++ b/packages/ndk/lib/domain_layer/entities/wallet/wallet.dart @@ -1,3 +1,8 @@ +import 'package:ndk/domain_layer/entities/wallet/wallet_balance.dart'; +import 'package:ndk/domain_layer/entities/wallet/wallet_transaction.dart'; +import 'package:rxdart/rxdart.dart'; + +import '../../usecases/nwc/nwc_connection.dart'; import '../cashu/cashu_mint_info.dart'; import 'wallet_type.dart'; @@ -94,6 +99,12 @@ class CashuWallet extends Wallet { class NwcWallet extends Wallet { final String nwcUrl; + NwcConnection? connection; + BehaviorSubject>? balanceSubject; + BehaviorSubject>? transactionsSubject; + BehaviorSubject>? pendingTransactionsSubject; + + bool isConnected() => connection != null; NwcWallet({ required super.id, diff --git a/packages/ndk/lib/domain_layer/repositories/wallets_repo.dart b/packages/ndk/lib/domain_layer/repositories/wallets_repo.dart index 849cc3c43..8a81f1e6f 100644 --- a/packages/ndk/lib/domain_layer/repositories/wallets_repo.dart +++ b/packages/ndk/lib/domain_layer/repositories/wallets_repo.dart @@ -8,10 +8,12 @@ abstract class WalletsRepo { Future getWallet(String id); Future addWallet(Wallet account); Future removeWallet(String id); - Stream> getBalancesStream(String accountId); + + Stream> getBalancesStream(String id); Stream> getPendingTransactionsStream( - String accountId); - Stream> getRecentTransactionsStream(String accountId); + String id); + Stream> getRecentTransactionsStream(String id); + Future> getTransactions({ int? limit, int? offset, diff --git a/packages/ndk/lib/domain_layer/usecases/nwc/nwc_notification.dart b/packages/ndk/lib/domain_layer/usecases/nwc/nwc_notification.dart index bb95bb60c..843341e2e 100644 --- a/packages/ndk/lib/domain_layer/usecases/nwc/nwc_notification.dart +++ b/packages/ndk/lib/domain_layer/usecases/nwc/nwc_notification.dart @@ -11,6 +11,7 @@ class NwcNotification { String? description; String? descriptionHash; String? preimage; + String? state; String paymentHash; int amount; int? feesPaid; @@ -24,6 +25,8 @@ class NwcNotification { bool get isPaymentReceived => notificationType == kPaymentReceived; bool get isPaymentSent => notificationType == kPaymentSent; bool get isHoldInvoiceAccepted => notificationType == kHoldInvoiceAccepted; + bool get isSettled => state == "settled"; + bool get isPending => state == "pending"; NwcNotification({ required this.notificationType, @@ -32,6 +35,7 @@ class NwcNotification { this.description, this.descriptionHash, this.preimage, + this.state, required this.paymentHash, required this.amount, this.feesPaid, @@ -51,6 +55,7 @@ class NwcNotification { description: map['description'] as String?, descriptionHash: map['description_hash'] as String?, preimage: map['preimage'] as String, + state: map['state'] as String?, paymentHash: map['payment_hash'] as String, amount: map['amount'] as int, feesPaid: map['fees_paid'] as int, @@ -66,6 +71,6 @@ class NwcNotification { @override toString() { - return 'NwcNotification{type: $type, invoice: $invoice, description: $description, descriptionHash: $descriptionHash, preimage: $preimage, paymentHash: $paymentHash, amount: $amount, feesPaid: $feesPaid, createdAt: $createdAt, expiresAt: $expiresAt, settledAt: $settledAt, metadata: $metadata}'; + return 'NwcNotification{type: $type, invoice: $invoice, state; $state description: $description, descriptionHash: $descriptionHash, preimage: $preimage, paymentHash: $paymentHash, amount: $amount, feesPaid: $feesPaid, createdAt: $createdAt, expiresAt: $expiresAt, settledAt: $settledAt, metadata: $metadata}'; } } diff --git a/packages/ndk/lib/domain_layer/usecases/nwc/responses/list_transactions_response.dart b/packages/ndk/lib/domain_layer/usecases/nwc/responses/list_transactions_response.dart index 1f789860a..90c8c3a39 100644 --- a/packages/ndk/lib/domain_layer/usecases/nwc/responses/list_transactions_response.dart +++ b/packages/ndk/lib/domain_layer/usecases/nwc/responses/list_transactions_response.dart @@ -46,7 +46,7 @@ class TransactionResult extends Equatable { /// The hash of the transaction description. final String? descriptionHash; - /// The hash of the transaction description. + /// can be "pending", "settled", "expired" (for invoices) or "failed" (for payments), optional final String? state; /// The preimage of the transaction. diff --git a/packages/ndk/lib/domain_layer/usecases/wallets/wallets.dart b/packages/ndk/lib/domain_layer/usecases/wallets/wallets.dart index 32f3b540b..6502bcc12 100644 --- a/packages/ndk/lib/domain_layer/usecases/wallets/wallets.dart +++ b/packages/ndk/lib/domain_layer/usecases/wallets/wallets.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:collection/collection.dart'; import 'package:rxdart/rxdart.dart'; import '../../entities/wallet/wallet_balance.dart'; @@ -46,10 +47,10 @@ class Wallets { _walletBalanceStreams = {}; final Map>> - _walletPendingStreams = {}; + _walletPendingTransactionStreams = {}; final Map>> - _walletRecentStreams = {}; + _walletRecentTransactionStreams = {}; /// stream subscriptions for cleanup final Map> _subscriptions = {}; @@ -99,7 +100,7 @@ class Wallets { if (defaultWalletId == null) { return null; } - return _wallets.firstWhere((wallet) => wallet.id == defaultWalletId); + return _wallets.firstWhereOrNull((wallet) => wallet.id == defaultWalletId); } Future _initializeWallet() async { @@ -123,14 +124,6 @@ class Wallets { _updateCombinedStreams(); } - void _ensureWalletStreamExists(String walletId) { - _walletBalanceStreams[walletId] ??= BehaviorSubject>(); - _walletPendingStreams[walletId] ??= - BehaviorSubject>(); - _walletRecentStreams[walletId] ??= - BehaviorSubject>(); - } - void _updateCombinedStreams() { // combine all wallet balances final allBalances = @@ -160,39 +153,9 @@ class Wallets { _walletsPendingTransactions[wallet.id] = []; _walletsRecentTransactions[wallet.id] = []; - // create individual streams if they don't exist - _ensureWalletStreamExists(wallet.id); - - // subscribe to repository streams and update in memory data - final subscriptions = []; - - // balance stream - subscriptions - .add(_walletsRepository.getBalancesStream(wallet.id).listen((balances) { - _walletsBalances[wallet.id] = balances; - _walletBalanceStreams[wallet.id]?.add(balances); - _updateCombinedStreams(); - })); - - // pending transactions stream - subscriptions.add(_walletsRepository - .getPendingTransactionsStream(wallet.id) - .listen((transactions) { - _walletsPendingTransactions[wallet.id] = transactions; - _walletPendingStreams[wallet.id]?.add(transactions); - _updateCombinedStreams(); - })); - - // recent transactions stream - subscriptions.add(_walletsRepository - .getRecentTransactionsStream(wallet.id) - .listen((transactions) { - _walletsRecentTransactions[wallet.id] = transactions; - _walletRecentStreams[wallet.id]?.add(transactions); - _updateCombinedStreams(); - })); - - _subscriptions[wallet.id] = subscriptions; + if (defaultWallet == null) { + setDefaultWallet(wallet.id); + } } /// add a new wallet to the system @@ -214,12 +177,12 @@ class Wallets { // clean up streams _walletBalanceStreams[walletId]?.close(); - _walletPendingStreams[walletId]?.close(); - _walletRecentStreams[walletId]?.close(); + _walletPendingTransactionStreams[walletId]?.close(); + _walletRecentTransactionStreams[walletId]?.close(); _walletBalanceStreams.remove(walletId); - _walletPendingStreams.remove(walletId); - _walletRecentStreams.remove(walletId); + _walletPendingTransactionStreams.remove(walletId); + _walletRecentTransactionStreams.remove(walletId); // clean up subscriptions _subscriptions[walletId]?.forEach((sub) => sub.cancel()); @@ -229,6 +192,10 @@ class Wallets { _walletsSubject.add(_wallets.toList()); _updateCombinedStreams(); + + if (walletId == defaultWalletId) { + defaultWalletId = _wallets.isNotEmpty ? _wallets.first.id : null; + } } /// set the default wallet to use by common operations \ @@ -241,6 +208,86 @@ class Wallets { } } + void _initBalanceStream(String id) { + if (_walletBalanceStreams[id] == null) { + _walletBalanceStreams[id] ??= BehaviorSubject>(); + final subscriptions = []; + subscriptions.add(_walletsRepository.getBalancesStream(id).listen((balances) { + _walletsBalances[id] = balances; + _walletBalanceStreams[id]?.add(balances); + _updateCombinedStreams(); + })); + if (_subscriptions[id] == null) { + _subscriptions[id] = subscriptions; + } else { + _subscriptions[id]?.addAll(subscriptions); + } + } + } + + void _initRecentTransactionStream(String id) { + if (_walletRecentTransactionStreams[id] == null) { + _walletRecentTransactionStreams[id] ??= + BehaviorSubject>(); + final subscriptions = []; + subscriptions.add(_walletsRepository.getRecentTransactionsStream(id).listen((transactions) { + transactions = transactions.where((tx) => tx.state.isDone).toList(); + _walletsRecentTransactions[id] = transactions; + _walletRecentTransactionStreams[id]?.add(transactions); + _updateCombinedStreams(); + })); + if (_subscriptions[id] == null) { + _subscriptions[id] = subscriptions; + } else { + _subscriptions[id]?.addAll(subscriptions); + } + } + } + + void _initPendingTransactionStream(String id) { + if (_walletPendingTransactionStreams[id] == null) { + _walletPendingTransactionStreams[id] ??= + BehaviorSubject>(); + final subscriptions = []; + subscriptions.add(_walletsRepository.getPendingTransactionsStream(id).listen((transactions) { + transactions = transactions.where((tx) => tx.state.isPending).toList(); + _walletsPendingTransactions[id] = transactions; + _walletPendingTransactionStreams[id]?.add(transactions); + _updateCombinedStreams(); + })); + if (_subscriptions[id] == null) { + _subscriptions[id] = subscriptions; + } else { + _subscriptions[id]?.addAll(subscriptions); + } + } + } + + Stream> getBalancesStream(String walletId) { + _initBalanceStream(walletId); + return _walletBalanceStreams[walletId]!.stream; + } + + Stream> getRecentTransactionsStream(String walletId) { + _initRecentTransactionStream(walletId); + return _walletRecentTransactionStreams[walletId]!.stream; + } + + Stream> getPendingTransactionsStream(String walletId) { + _initPendingTransactionStream(walletId); + return _walletPendingTransactionStreams[walletId]!.stream; + } + + int getBalance(String walletId, String unit) { + _initBalanceStream(walletId); + final balances = _walletsBalances[walletId]; + if (balances == null) { + return 0; + } + final balance = balances.firstWhereOrNull((balance) => balance.unit == unit); + return balance?.amount ?? 0; + } + /// calculate combined balance for a specific currency int getCombinedBalance(String unit) { return _walletsBalances.values @@ -277,10 +324,10 @@ class Wallets { for (final stream in _walletBalanceStreams.values) { futures.add(stream.close()); } - for (final stream in _walletPendingStreams.values) { + for (final stream in _walletPendingTransactionStreams.values) { futures.add(stream.close()); } - for (final stream in _walletRecentStreams.values) { + for (final stream in _walletRecentTransactionStreams.values) { futures.add(stream.close()); } @@ -291,8 +338,8 @@ class Wallets { _walletsPendingTransactions.clear(); _walletsRecentTransactions.clear(); _walletBalanceStreams.clear(); - _walletPendingStreams.clear(); - _walletRecentStreams.clear(); + _walletPendingTransactionStreams.clear(); + _walletRecentTransactionStreams.clear(); _subscriptions.clear(); defaultWalletId = null; } diff --git a/packages/ndk/pubspec.lock b/packages/ndk/pubspec.lock index 1032e995f..69d0b0d97 100644 --- a/packages/ndk/pubspec.lock +++ b/packages/ndk/pubspec.lock @@ -306,6 +306,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.2" + hex: + dependency: transitive + description: + name: hex + sha256: "4e7cd54e4b59ba026432a6be2dd9d96e4c5205725194997193bf871703b82c4a" + url: "https://pub.dev" + source: hosted + version: "0.2.0" http: dependency: "direct main" description: diff --git a/packages/sample-app/lib/main.dart b/packages/sample-app/lib/main.dart index 92adcb9c6..3a891f5ee 100644 --- a/packages/sample-app/lib/main.dart +++ b/packages/sample-app/lib/main.dart @@ -167,7 +167,7 @@ class _MyHomePageState extends State // The main change is how _tabPages is constructed in build() to pass the callback. _tabController = TabController( - length: 7, + length: _tabs.length, vsync: this); // Fixed length to 5 (Accounts, Metadata, Relays, NWC, Blossom) _tabController.addListener(() { diff --git a/packages/sample-app/lib/wallets.dart b/packages/sample-app/lib/wallets.dart index 725643e3d..ab1f08333 100644 --- a/packages/sample-app/lib/wallets.dart +++ b/packages/sample-app/lib/wallets.dart @@ -209,6 +209,9 @@ class _WalletsPageState extends State { nwcUrl: _nwcUriController.text, ); await widget.ndk.wallets.addWallet(nwcWallet); + widget.ndk.wallets.getBalance(walletId, "sat"); + widget.ndk.wallets.getRecentTransactionsStream(walletId); + widget.ndk.wallets.getPendingTransactionsStream(walletId); Navigator.of(context).pop(); } catch (e) { displayError(e.toString()); diff --git a/packages/sample-app/pubspec.lock b/packages/sample-app/pubspec.lock index fcf0adce3..c36af3b42 100644 --- a/packages/sample-app/pubspec.lock +++ b/packages/sample-app/pubspec.lock @@ -25,6 +25,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.7.0" + ascii_qr: + dependency: transitive + description: + name: ascii_qr + sha256: "2046e400a0fa4ea0de5df44c87b992cdd1f76403bb15e64513b89263598750ae" + url: "https://pub.dev" + source: hosted + version: "1.0.1" async: dependency: transitive description: @@ -41,6 +49,15 @@ packages: url: "https://pub.dev" source: hosted version: "0.2.2" + bip32_keys: + dependency: transitive + description: + path: "." + ref: HEAD + resolved-ref: b5a0342220e7ee5aaf64d489a589bdee6ef8de22 + url: "https://github.com/1-leo/dart-bip32-keys" + source: git + version: "3.1.2" bip340: dependency: transitive description: @@ -49,6 +66,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.3.1" + bip39_mnemonic: + dependency: transitive + description: + name: bip39_mnemonic + sha256: dd6bdfc2547d986b2c00f99bba209c69c0b6fa5c1a185e1f728998282f1249d5 + url: "https://pub.dev" + source: hosted + version: "4.0.1" boolean_selector: dependency: transitive description: @@ -57,6 +82,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" + bs58check: + dependency: transitive + description: + name: bs58check + sha256: c4a164d42b25c2f6bc88a8beccb9fc7d01440f3c60ba23663a20a70faf484ea9 + url: "https://pub.dev" + source: hosted + version: "1.0.2" build_cli_annotations: dependency: transitive description: @@ -65,6 +98,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.0" + cbor: + dependency: transitive + description: + name: cbor + sha256: f5239dd6b6ad24df67d1449e87d7180727d6f43b87b3c9402e6398c7a2d9609b + url: "https://pub.dev" + source: hosted + version: "6.3.7" characters: dependency: transitive description: @@ -248,6 +289,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.2" + ieee754: + dependency: transitive + description: + name: ieee754 + sha256: "7d87451c164a56c156180d34a4e93779372edd191d2c219206100b976203128c" + url: "https://pub.dev" + source: hosted + version: "1.0.3" image: dependency: transitive description: @@ -414,21 +463,21 @@ packages: path: "../ndk" relative: true source: path - version: "0.6.0-dev.12" + version: "0.6.1-dev.1" ndk_amber: dependency: "direct main" description: path: "../amber" relative: true source: path - version: "0.3.3-dev.15" + version: "0.3.3-dev.1" ndk_rust_verifier: dependency: "direct main" description: path: "../rust_verifier" relative: true source: path - version: "0.4.2-dev.17" + version: "0.4.2-dev.1" nested: dependency: transitive description: @@ -794,6 +843,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + unorm_dart: + dependency: transitive + description: + name: unorm_dart + sha256: "0c69186b03ca6addab0774bcc0f4f17b88d4ce78d9d4d8f0619e30a99ead58e7" + url: "https://pub.dev" + source: hosted + version: "0.3.2" uri_parser: dependency: transitive description: