Make restore height optional, use polyseed restore date and add bip39 support
What changed, and why it matters
This commit changes how users restore a Monero wallet in the Skylight Wallet app. It makes the 'restore height' field optional, automatically fills it for newer Polyseed seeds, and adds support for restoring from BIP39-style mnemonic phrases (common 12/24-word seeds). The changes are mostly usability improvements, but they touch sensitive wallet-recovery code and introduce new dependencies for seed handling. There is no direct evidence in the commit of a security vulnerability, but the new BIP39 conversion logic and the changed restore flow deserve careful review because mistakes here could lead to users restoring the wrong wallet or leaking keys.
Treat this as a code-review priority area rather than an incident. Independently verify that the BIP39 derivation path and Ed25519 reduction in `lib/util/bip39.dart` match Monero's expected spend-key format and do not produce weak or colliding keys. Confirm that defaulting restore height to 0 cannot cause users to miss funds or accidentally expose transaction history. Review the new dependencies for supply-chain risk and ensure the `hashlib` override does not break other cryptographic assumptions. No patch or emergency response is indicated by the diff alone.
Security signals we found
New cryptographic key derivation code added (BIP39 -> BIP32 -> Monero spend key)
Restore height now optional and defaults to 0 when parsing fails
New third-party dependencies introduced for seed and key derivation
Polyseed birthday timestamp converted to restore height via a hard-coded month-to-height mapping
Sensitive wallet-recovery flow refactored with changed error handling
Evidence from the diff
The patch refactors WalletModel.restoreFromMnemonic to detect seed type (Polyseed, BIP39, or legacy Monero) and route accordingly. It adds lib/util/bip39.dart, which derives a Monero spend key from a BIP39 mnemonic via BIP32 path m/44'/128'/{accountIndex}'/0/0 and reduces it modulo the Ed25519 curve order, then encodes it as a legacy Monero seed phrase. It also adds lib/util/get_height_by_date.dart to estimate a block height from a Polyseed birthday timestamp. The UI now allows an empty restore height (defaulting to 0) and auto-populates the height when a Polyseed is detected. New pub dependencies: polyseed, bip39, bip32, plus a hashlib override.
Changed components
lib/models/wallet_model.dartlib/screens/restore_wallet.dartlib/util/bip39.dartlib/util/get_height_by_date.dartpubspec.yamlInspect captured patch +389 / −50
diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb
index c243a9b..8ab32f4 100644
--- a/lib/l10n/app_en.arb
+++ b/lib/l10n/app_en.arb
@@ -42,7 +42,7 @@
"restoreWalletTitle": "Restore Wallet",
"restoreWalletDescription": "Input your Monero seed below. We will check common formats.",
"restoreWalletSeedLabel": "Seed",
- "restoreWalletRestoreHeightLabel": "Restore Height",
+ "restoreWalletRestoreHeightLabel": "Restore Height (optional)",
"restoreWalletRestoreButton": "Restore",
"restoreWalletInvalidMnemonic": "Invalid seed.",
"navigationBarWallet": "Wallet",
diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart
index 62fdd2a..0823eda 100644
--- a/lib/l10n/app_localizations.dart
+++ b/lib/l10n/app_localizations.dart
@@ -348,7 +348,7 @@ abstract class AppLocalizations {
/// No description provided for @restoreWalletRestoreHeightLabel.
///
/// In en, this message translates to:
- /// **'Restore Height'**
+ /// **'Restore Height (optional)'**
String get restoreWalletRestoreHeightLabel;
/// No description provided for @restoreWalletRestoreButton.
diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart
index 2c9de83..80893e8 100644
--- a/lib/l10n/app_localizations_en.dart
+++ b/lib/l10n/app_localizations_en.dart
@@ -142,7 +142,7 @@ class AppLocalizationsEn extends AppLocalizations {
String get restoreWalletSeedLabel => 'Seed';
@override
- String get restoreWalletRestoreHeightLabel => 'Restore Height';
+ String get restoreWalletRestoreHeightLabel => 'Restore Height (optional)';
@override
String get restoreWalletRestoreButton => 'Restore';
diff --git a/lib/l10n/app_localizations_pt.dart b/lib/l10n/app_localizations_pt.dart
index 7163224..d57077d 100644
--- a/lib/l10n/app_localizations_pt.dart
+++ b/lib/l10n/app_localizations_pt.dart
@@ -142,7 +142,7 @@ class AppLocalizationsPt extends AppLocalizations {
String get restoreWalletSeedLabel => 'Semente';
@override
- String get restoreWalletRestoreHeightLabel => 'Bloco de Restauração';
+ String get restoreWalletRestoreHeightLabel => 'Bloco de Restauração (opcional)';
@override
String get restoreWalletRestoreButton => 'Restaurar';
diff --git a/lib/l10n/app_pt.arb b/lib/l10n/app_pt.arb
index dd152da..8d3222b 100644
--- a/lib/l10n/app_pt.arb
+++ b/lib/l10n/app_pt.arb
@@ -42,7 +42,7 @@
"restoreWalletTitle": "Restaurar Carteira",
"restoreWalletDescription": "Insira sua semente Monero abaixo. Verificaremos os formatos comuns.",
"restoreWalletSeedLabel": "Semente",
- "restoreWalletRestoreHeightLabel": "Bloco de Restauração",
+ "restoreWalletRestoreHeightLabel": "Bloco de Restauração (opcional)",
"restoreWalletRestoreButton": "Restaurar",
"restoreWalletInvalidMnemonic": "Semente inválida.",
"navigationBarWallet": "Carteira",
diff --git a/lib/models/wallet_model.dart b/lib/models/wallet_model.dart
index ee9ed36..32cdd45 100644
--- a/lib/models/wallet_model.dart
+++ b/lib/models/wallet_model.dart
@@ -1,7 +1,4 @@
// ignore_for_file: implementation_imports
-
-// import 'dart:async';
-// import 'dart:async' show Timer;
import 'dart:async';
import 'dart:convert';
import 'dart:ffi';
@@ -15,10 +12,13 @@ import 'package:monero/monero.dart' as monero;
import 'package:monero/src/monero.dart';
import 'package:monero/src/wallet2.dart';
import 'package:http/http.dart' as http;
+import 'package:polyseed/polyseed.dart';
+import 'package:bip39/bip39.dart' as bip39;
import 'package:skylight_wallet/services/notifications_service.dart';
import 'package:skylight_wallet/services/shared_preferences_service.dart';
import 'package:skylight_wallet/services/tor_service.dart';
+import 'package:skylight_wallet/util/bip39.dart';
import 'package:skylight_wallet/util/cacert.dart';
import 'package:skylight_wallet/util/formatting.dart';
import 'package:skylight_wallet/util/height.dart';
@@ -713,57 +713,49 @@ class WalletModel with ChangeNotifier {
int restoreHeight, [
String passphrase = '',
]) async {
- final legacyWallet = await _getWalletFromLegacySeed(
- mnemonic: mnemonic,
- restoreHeight: restoreHeight,
- password: '',
- isDummy: true,
- );
-
- final polyseedWallet = await _getWalletFromPolyseed(
- mnemonic: mnemonic,
- restoreHeight: restoreHeight,
- password: '',
- isDummy: true,
- );
-
final walletPassword = _desktopWalletPassword ?? genWalletPassword();
+ MoneroWallet? wallet;
- if ((legacyWallet.errorString() == '' && legacyWallet.status() == 0) ||
- legacyWallet.errorString() == 'No response from HTTP server' &&
- legacyWallet.status() == 1) {
- _w2Wallet = await _getWalletFromLegacySeed(
+ if (Polyseed.isValidSeed(mnemonic)) {
+ wallet = await _getWalletFromPolyseed(
mnemonic: mnemonic,
restoreHeight: restoreHeight,
password: walletPassword,
);
- } else if ((polyseedWallet.errorString() == '' && polyseedWallet.status() == 0) ||
- polyseedWallet.errorString() == 'No response from HTTP server' &&
- polyseedWallet.status() == 1) {
- _w2Wallet = await _getWalletFromPolyseed(
+ } else if (bip39.validateMnemonic(mnemonic)) {
+ final legacyMnemonic = getLegacySeedFromBip39(mnemonic);
+
+ wallet = await _getWalletFromLegacySeed(
+ mnemonic: legacyMnemonic,
+ restoreHeight: restoreHeight,
+ password: walletPassword,
+ );
+ } else {
+ wallet = await _getWalletFromLegacySeed(
mnemonic: mnemonic,
restoreHeight: restoreHeight,
password: walletPassword,
);
}
- if (_w2Wallet == null &&
- legacyWallet.errorString().contains('word list failed verification') &&
- polyseedWallet.errorString().contains('Failed polyseed decode')) {
- throw Exception('Invalid mnemonic.');
- } else if (_w2Wallet == null) {
- log(LogLevel.error, 'Something went wrong when restoring from mnemonic.');
- log(LogLevel.error, 'Legacy wallet error: ${legacyWallet.errorString()}');
- log(LogLevel.error, 'Polyseed wallet error: ${polyseedWallet.errorString()}');
- throw Exception('Something went wrong.');
+ if ((wallet.errorString() != '' || wallet.status() != 0) &&
+ !wallet.errorString().contains('No response from HTTP server')) {
+ if (wallet.errorString().contains('word list failed verification') ||
+ wallet.errorString().contains('Failed polyseed decode')) {
+ throw Exception('Invalid mnemonic.');
+ }
+
+ log(LogLevel.error, 'Error restoring from mnemonic: ${wallet.errorString()}');
+ throw Exception('Error restoring from mnemonic: ${wallet.errorString()}');
}
+ _w2Wallet = wallet;
+ _w2TxHistory = _w2Wallet!.history();
+
if (Platform.isAndroid || Platform.isIOS) {
await storeMobileWalletPassword(walletPassword);
}
- _w2TxHistory = _w2Wallet!.history();
-
await store();
notifyListeners();
}
diff --git a/lib/screens/restore_wallet.dart b/lib/screens/restore_wallet.dart
index 0bb591c..bf6a44a 100644
--- a/lib/screens/restore_wallet.dart
+++ b/lib/screens/restore_wallet.dart
@@ -1,8 +1,11 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
+import 'package:provider/provider.dart';
+import 'package:polyseed/polyseed.dart';
+
import 'package:skylight_wallet/l10n/app_localizations.dart';
+import 'package:skylight_wallet/util/get_height_by_date.dart';
import 'package:skylight_wallet/util/logging.dart';
-import 'package:provider/provider.dart';
import 'package:skylight_wallet/models/wallet_model.dart';
class RestoreWalletScreen extends StatefulWidget {
@@ -15,10 +18,18 @@ class RestoreWalletScreen extends StatefulWidget {
class _RestoreWalletScreenState extends State<RestoreWalletScreen> {
final _mnemonicController = TextEditingController();
final _restoreHeightController = TextEditingController();
+ bool _isPolyseed = false;
bool _isLoading = false;
String? _mnemonicError;
String? _restoreHeightError;
+ @override
+ void dispose() {
+ _mnemonicController.dispose();
+ _restoreHeightController.dispose();
+ super.dispose();
+ }
+
Future<void> _restore() async {
if (_isLoading) return;
@@ -36,17 +47,10 @@ class _RestoreWalletScreenState extends State<RestoreWalletScreen> {
return;
}
- if (_restoreHeightController.text.isEmpty) {
- setState(() {
- _restoreHeightError = i18n.fieldEmptyError;
- });
- return;
- }
-
final wallet = Provider.of<WalletModel>(context, listen: false);
final mnemonic = _mnemonicController.text;
- final restoreHeight = int.parse(_restoreHeightController.text);
+ final restoreHeight = int.tryParse(_restoreHeightController.text) ?? 0;
setState(() {
_isLoading = true;
@@ -102,6 +106,52 @@ class _RestoreWalletScreenState extends State<RestoreWalletScreen> {
}
}
+ Future<void> _calculatePolyseedHeight() async {
+ if (!Polyseed.isValidSeed(_mnemonicController.text)) {
+ if (_isPolyseed) {
+ setState(() {
+ _isPolyseed = false;
+ _restoreHeightController.text = '';
+ });
+ }
+ return;
+ }
+
+ final polyseed = Polyseed.decode(
+ _mnemonicController.text,
+ PolyseedLang.getByPhrase(_mnemonicController.text),
+ PolyseedCoin.POLYSEED_MONERO,
+ );
+
+ final birthday = polyseed.birthday;
+ final restoreHeight = getHeightByDate(
+ date: DateTime.fromMillisecondsSinceEpoch(birthday * 1000),
+ );
+
+ setState(() {
+ _isPolyseed = true;
+ _restoreHeightController.text = restoreHeight.toString();
+ });
+ }
+
+ void _onMnemonicChanged(String value) {
+ _calculatePolyseedHeight();
+
+ if (_mnemonicError != null) {
+ setState(() {
+ _mnemonicError = null;
+ });
+ }
+ }
+
+ void _onRestoreHeightChanged(String value) {
+ if (_restoreHeightError != null) {
+ setState(() {
+ _restoreHeightError = null;
+ });
+ }
+ }
+
@override
Widget build(BuildContext context) {
final i18n = AppLocalizations.of(context)!;
@@ -129,6 +179,7 @@ class _RestoreWalletScreenState extends State<RestoreWalletScreen> {
padding: EdgeInsets.symmetric(horizontal: 20),
child: TextFormField(
controller: _mnemonicController,
+ onChanged: _onMnemonicChanged,
keyboardType: TextInputType.multiline,
maxLines: null,
minLines: 3,
@@ -143,6 +194,7 @@ class _RestoreWalletScreenState extends State<RestoreWalletScreen> {
padding: EdgeInsets.symmetric(horizontal: 20),
child: TextFormField(
controller: _restoreHeightController,
+ onChanged: _onRestoreHeightChanged,
keyboardType: TextInputType.number,
inputFormatters: <TextInputFormatter>[FilteringTextInputFormatter.digitsOnly],
decoration: InputDecoration(
diff --git a/lib/util/bip39.dart b/lib/util/bip39.dart
new file mode 100644
index 0000000..f8e9019
--- /dev/null
+++ b/lib/util/bip39.dart
@@ -0,0 +1,49 @@
+// From https://github.com/cake-tech/cake_wallet/blob/main/cw_monero/lib/bip39_seed.dart
+
+import 'dart:typed_data';
+import 'package:bip32/bip32.dart' as bip32;
+import 'package:bip39/bip39.dart' as bip39;
+import 'package:polyseed/polyseed.dart';
+
+String getLegacySeedFromBip39(String mnemonic, {int accountIndex = 0, String passphrase = ""}) {
+ final seed = bip39.mnemonicToSeed(mnemonic, passphrase: passphrase);
+ final bip32KeyPair = bip32.BIP32.fromSeed(seed).derivePath("m/44'/128'/$accountIndex'/0/0");
+ final spendKey = _reduceECKey(bip32KeyPair.privateKey!);
+
+ return LegacySeedLang.getByEnglishName("English").encodePhrase(spendKey.toHexString());
+}
+
+const _ed25519CurveOrder = "1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED";
+
+Uint8List _reduceECKey(Uint8List buffer) {
+ final curveOrder = BigInt.parse(_ed25519CurveOrder, radix: 16);
+ final bigNumber = _readBytes(buffer);
+
+ var result = bigNumber % curveOrder;
+
+ final resultBuffer = Uint8List(32);
+ for (var i = 0; i < 32; i++) {
+ resultBuffer[i] = (result & BigInt.from(0xff)).toInt();
+ result = result >> 8;
+ }
+
+ return resultBuffer;
+}
+
+/// Read BigInt from a little-endian Uint8List
+/// From https://github.com/dart-lang/sdk/issues/32803#issuecomment-387405784
+BigInt _readBytes(Uint8List bytes) {
+ BigInt read(int start, int end) {
+ if (end - start <= 4) {
+ var result = 0;
+ for (int i = end - 1; i >= start; i--) {
+ result = result * 256 + bytes[i];
+ }
+ return BigInt.from(result);
+ }
+ final mid = start + ((end - start) >> 1);
+ return read(start, mid) + read(mid, end) * (BigInt.one << ((mid - start) * 8));
+ }
+
+ return read(0, bytes.length);
+}
diff --git a/lib/util/get_height_by_date.dart b/lib/util/get_height_by_date.dart
new file mode 100644
index 0000000..6e3a2d5
--- /dev/null
+++ b/lib/util/get_height_by_date.dart
@@ -0,0 +1,160 @@
+// From https://github.com/cake-tech/cake_wallet/blob/main/cw_core/lib/get_height_by_date.dart
+
+import 'package:intl/intl.dart';
+import 'package:skylight_wallet/util/logging.dart';
+
+final dateFormat = DateFormat('yyyy-MM');
+
+final dates = {
+ "2014-5": 18844,
+ "2014-6": 65406,
+ "2014-7": 108882,
+ "2014-8": 153594,
+ "2014-9": 198072,
+ "2014-10": 241088,
+ "2014-11": 285305,
+ "2014-12": 328069,
+ "2015-1": 372369,
+ "2015-2": 416505,
+ "2015-3": 456631,
+ "2015-4": 501084,
+ "2015-5": 543973,
+ "2015-6": 588326,
+ "2015-7": 631187,
+ "2015-8": 675484,
+ "2015-9": 719725,
+ "2015-10": 762463,
+ "2015-11": 806528,
+ "2015-12": 849041,
+ "2016-1": 892866,
+ "2016-2": 936736,
+ "2016-3": 977691,
+ "2016-4": 1015848,
+ "2016-5": 1037417,
+ "2016-6": 1059651,
+ "2016-7": 1081269,
+ "2016-8": 1103630,
+ "2016-9": 1125983,
+ "2016-10": 1147617,
+ "2016-11": 1169779,
+ "2016-12": 1191402,
+ "2017-1": 1213861,
+ "2017-2": 1236197,
+ "2017-3": 1256358,
+ "2017-4": 1278622,
+ "2017-5": 1300239,
+ "2017-6": 1322564,
+ "2017-7": 1344225,
+ "2017-8": 1366664,
+ "2017-9": 1389113,
+ "2017-10": 1410738,
+ "2017-11": 1433039,
+ "2017-12": 1454639,
+ "2018-1": 1477201,
+ "2018-2": 1499599,
+ "2018-3": 1519796,
+ "2018-4": 1542067,
+ "2018-5": 1562861,
+ "2018-6": 1585135,
+ "2018-7": 1606715,
+ "2018-8": 1629017,
+ "2018-9": 1651347,
+ "2018-10": 1673031,
+ "2018-11": 1695128,
+ "2018-12": 1716687,
+ "2019-1": 1738923,
+ "2019-2": 1761435,
+ "2019-3": 1781681,
+ "2019-4": 1803081,
+ "2019-5": 1824671,
+ "2019-6": 1847005,
+ "2019-7": 1868590,
+ "2019-8": 1890552,
+ "2019-9": 1912212,
+ "2019-10": 1932200,
+ "2019-11": 1957040,
+ "2019-12": 1978090,
+ "2020-1": 2001290,
+ "2020-2": 2022688,
+ "2020-3": 2043987,
+ "2020-4": 2066536,
+ "2020-5": 2090797,
+ "2020-6": 2111633,
+ "2020-7": 2131433,
+ "2020-8": 2153983,
+ "2020-9": 2176466,
+ "2020-10": 2198453,
+ "2020-11": 2220000,
+ "2020-12": 2242240,
+ "2021-1": 2264584,
+ "2021-2": 2286892,
+ "2021-3": 2307079,
+ "2021-4": 2329385,
+ "2021-5": 2351004,
+ "2021-6": 2373306,
+ "2021-7": 2394882,
+ "2021-8": 2417162,
+ "2021-9": 2439490,
+ "2021-10": 2461020,
+ "2021-11": 2483377,
+ "2021-12": 2504932,
+ "2022-1": 2527316,
+ "2022-2": 2549605,
+ "2022-3": 2569711,
+ "2022-4": 2591995,
+ "2022-5": 2613603,
+ "2022-6": 2635840,
+ "2022-7": 2657395,
+ "2022-8": 2679705,
+ "2022-9": 2701991,
+ "2022-10": 2723607,
+ "2022-11": 2745899,
+ "2022-12": 2767427,
+ "2023-1": 2789763,
+ "2023-2": 2811996,
+ "2023-3": 2832118,
+ "2023-4": 2854365,
+ "2023-5": 2875972,
+ "2023-6": 2898234,
+ "2023-7": 2919771,
+ "2023-8": 2942045,
+ "2023-9": 2964280,
+ "2023-10": 2985937,
+ "2023-11": 3008178,
+ "2023-12": 3029759,
+};
+
+int getHeightByDate({required DateTime date}) {
+ final raw = '${date.year}-${date.month}';
+ final lastHeight = dates.values.last;
+ int startHeight;
+ int endHeight;
+ int height = 0;
+
+ try {
+ if ((dates[raw] == null) || (dates[raw] == lastHeight)) {
+ startHeight = dates.values.toList()[dates.length - 2];
+ endHeight = dates.values.toList()[dates.length - 1];
+ final heightPerDay = (endHeight - startHeight) / 31;
+ final endDateRaw = dates.keys.toList()[dates.length - 1].split('-');
+ final endYear = int.parse(endDateRaw[0]);
+ final endMonth = int.parse(endDateRaw[1]);
+ final endDate = DateTime(endYear, endMonth);
+ final differenceInDays = date.difference(endDate).inDays;
+ final daysHeight = (differenceInDays * heightPerDay).round();
+ height = endHeight + daysHeight;
+ } else {
+ startHeight = dates[raw]!;
+ final index = dates.values.toList().indexOf(startHeight);
+ endHeight = dates.values.toList()[index + 1];
+ final heightPerDay = ((endHeight - startHeight) / 31).round();
+ final daysHeight = (date.day - 1) * heightPerDay;
+ height = startHeight + daysHeight - heightPerDay;
+ }
+ } catch (e) {
+ log(LogLevel.error, e.toString());
+ rethrow;
+ }
+
+ return height;
+}
diff --git a/pubspec.lock b/pubspec.lock
index 462ccc2..ad9b960 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -25,6 +25,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.13.0"
+ bip32:
+ dependency: "direct main"
+ description:
+ name: bip32
+ sha256: "54787cd7a111e9d37394aabbf53d1fc5e2e0e0af2cd01c459147a97c0e3f8a97"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.0.0"
+ bip39:
+ dependency: "direct main"
+ description:
+ name: bip39
+ sha256: de1ee27ebe7d96b84bb3a04a4132a0a3007dcdd5ad27dd14aa87a29d97c45edc
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.6"
boolean_selector:
dependency: transitive
description:
@@ -33,6 +49,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"
characters:
dependency: transitive
description:
@@ -73,6 +97,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.19.1"
+ convert:
+ dependency: transitive
+ description:
+ name: convert
+ sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.1.2"
cross_file:
dependency: transitive
description:
@@ -301,6 +333,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.4"
+ hashlib:
+ dependency: "direct overridden"
+ description:
+ name: hashlib
+ sha256: d41795742c10947930630118c6836608deeb9047cd05aee32d2baeb697afd66a
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.19.2"
+ hashlib_codecs:
+ dependency: transitive
+ description:
+ name: hashlib_codecs
+ sha256: "8cea9ccafcfeaa7324d2ae52c61c69f7ff71f4237507a018caab31b9e416e3b1"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.6.0"
+ hex:
+ dependency: transitive
+ description:
+ name: hex
+ sha256: "4e7cd54e4b59ba026432a6be2dd9d96e4c5205725194997193bf871703b82c4a"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.2.0"
http:
dependency: "direct main"
description:
@@ -614,6 +670,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.8"
+ pointycastle:
+ dependency: transitive
+ description:
+ name: pointycastle
+ sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.9.1"
+ polyseed:
+ dependency: "direct main"
+ description:
+ name: polyseed
+ sha256: ccc6ba0ed3976837e770a324a6295fbf3d7c0d05fc0fa1906e36479b07c2675b
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.0.7"
posix:
dependency: transitive
description:
@@ -892,6 +964,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.0"
+ unorm_dart:
+ dependency: transitive
+ description:
+ name: unorm_dart
+ sha256: "0c69186b03ca6addab0774bcc0f4f17b88d4ce78d9d4d8f0619e30a99ead58e7"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.3.2"
url_launcher_linux:
dependency: transitive
description:
diff --git a/pubspec.yaml b/pubspec.yaml
index aef85d8..85a33a6 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -77,6 +77,12 @@ dependencies:
local_auth: 2.3.0
flutter_launcher_icons: 0.14.4
gpt_markdown: 1.1.4
+ polyseed: 0.0.7
+ bip39: 1.0.6
+ bip32: 2.0.0
+
+dependency_overrides:
+ hashlib: 1.19.2
dev_dependencies:
flutter_test:
Why this scored 23/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.