diff --git a/assets/translations/en.json b/assets/translations/en.json index 37fb974d1c..bd51adab5d 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -380,6 +380,9 @@ "dexErrorMessage": "Something went wrong!", "seedConfirmInitialText": "Enter the seed phrase", "seedConfirmIncorrectText": "Incorrect seed phrase", + "mnemonicInvalidWordError": "Your seed phrase contains an unknown word. Please check spelling and try again.", + "mnemonicInvalidChecksumError": "Your seed phrase appears valid but the checksum doesn't match. Check the word order and spacing.", + "mnemonicInvalidLengthError": "Seed phrase must contain between {} and {} words.", "usedSamePassword": "This password matches your current one. Please create a different password.", "passwordNotAccepted": "Password not accepted", "confirmNewPassword": "Confirm new password", @@ -737,4 +740,4 @@ "fetchingPrivateKeysMessage": "Please wait while we securely fetch your private keys...", "pubkeyType": "Type", "securitySettings": "Security Settings" -} +} \ No newline at end of file diff --git a/lib/bloc/coin_addresses/bloc/coin_addresses_bloc.dart b/lib/bloc/coin_addresses/bloc/coin_addresses_bloc.dart index bfa2e15007..2081925f38 100644 --- a/lib/bloc/coin_addresses/bloc/coin_addresses_bloc.dart +++ b/lib/bloc/coin_addresses/bloc/coin_addresses_bloc.dart @@ -2,8 +2,8 @@ import 'dart:async'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:komodo_defi_sdk/komodo_defi_sdk.dart'; -import 'package:komodo_defi_types/komodo_defi_types.dart' show Asset; -import 'package:komodo_defi_types/komodo_defi_types.dart' show NewAddressStatus; +import 'package:komodo_defi_types/komodo_defi_types.dart' + show Asset, NewAddressStatus, AssetPubkeys; import 'package:web_dex/analytics/events.dart'; import 'package:web_dex/bloc/analytics/analytics_bloc.dart'; import 'package:web_dex/bloc/coin_addresses/bloc/coin_addresses_event.dart'; @@ -15,7 +15,7 @@ class CoinAddressesBloc extends Bloc { final String assetId; final AnalyticsBloc analyticsBloc; - StreamSubscription? _pubkeysSub; + StreamSubscription? _pubkeysSub; CoinAddressesBloc(this.sdk, this.assetId, this.analyticsBloc) : super(const CoinAddressesState()) { on(_onCreateAddressSubmitted); @@ -43,57 +43,71 @@ class CoinAddressesBloc extends Bloc { newAddressState: () => null, ), ); - - final stream = sdk.pubkeys.createNewPubkeyStream(getSdkAsset(sdk, assetId)); - - await for (final newAddressState in stream) { - emit(state.copyWith(newAddressState: () => newAddressState)); - - switch (newAddressState.status) { - case NewAddressStatus.completed: - final pubkey = newAddressState.address; - final derivation = pubkey?.derivationPath; - if (derivation != null) { - final parsed = parseDerivationPath(derivation); - analyticsBloc.logEvent( - HdAddressGeneratedEventData( - accountIndex: parsed.accountIndex, - addressIndex: parsed.addressIndex, - assetSymbol: assetId, + try { + final asset = getSdkAsset(sdk, assetId); + final stream = sdk.pubkeys.watchCreateNewPubkey(asset); + + await for (final newAddressState in stream) { + emit(state.copyWith(newAddressState: () => newAddressState)); + + switch (newAddressState.status) { + case NewAddressStatus.completed: + final pubkey = newAddressState.address; + final derivation = pubkey?.derivationPath; + if (derivation != null) { + try { + final parsed = parseDerivationPath(derivation); + analyticsBloc.logEvent( + HdAddressGeneratedEventData( + accountIndex: parsed.accountIndex, + addressIndex: parsed.addressIndex, + assetSymbol: assetId, + ), + ); + } catch (_) { + // Non-fatal: continue without analytics if derivation parsing fails + } + } + + add(const CoinAddressesSubscriptionRequested()); + + emit( + state.copyWith( + createAddressStatus: () => FormStatus.success, + newAddressState: () => null, ), ); - } - - add(const CoinAddressesSubscriptionRequested()); - - emit( - state.copyWith( - createAddressStatus: () => FormStatus.success, - newAddressState: () => null, - ), - ); - return; - case NewAddressStatus.error: - emit( - state.copyWith( - createAddressStatus: () => FormStatus.failure, - errorMessage: () => newAddressState.error, - newAddressState: () => null, - ), - ); - return; - case NewAddressStatus.cancelled: - emit( - state.copyWith( - createAddressStatus: () => FormStatus.initial, - newAddressState: () => null, - ), - ); - return; - default: - // continue listening for next events - break; + return; + case NewAddressStatus.error: + emit( + state.copyWith( + createAddressStatus: () => FormStatus.failure, + errorMessage: () => newAddressState.error, + newAddressState: () => null, + ), + ); + return; + case NewAddressStatus.cancelled: + emit( + state.copyWith( + createAddressStatus: () => FormStatus.initial, + newAddressState: () => null, + ), + ); + return; + default: + // continue listening for next events + break; + } } + } catch (e) { + emit( + state.copyWith( + createAddressStatus: () => FormStatus.failure, + errorMessage: () => e.toString(), + newAddressState: () => null, + ), + ); } } @@ -114,10 +128,11 @@ class CoinAddressesBloc extends Bloc { status: () => FormStatus.success, addresses: () => addresses, cantCreateNewAddressReasons: () => reasons, + errorMessage: () => null, ), ); - _startWatchingPubkeys(asset); + await _startWatchingPubkeys(asset); } catch (e) { emit( state.copyWith( @@ -159,26 +174,42 @@ class CoinAddressesBloc extends Bloc { CoinAddressesPubkeysSubscriptionFailed event, Emitter emit, ) { - emit(state.copyWith(errorMessage: () => event.error)); + emit( + state.copyWith( + status: () => FormStatus.failure, + errorMessage: () => event.error, + ), + ); } - void _startWatchingPubkeys(Asset asset) { - _pubkeysSub?.cancel(); - // pre-cache pubkeys to ensure that any newly created pubkeys are available - // when we start watching. UI flickering between old and new states is - // avoided this way. The watchPubkeys function yields the last known pubkeys - // when the pubkeys stream is first activated. - sdk.pubkeys.preCachePubkeys(asset); - _pubkeysSub = sdk.pubkeys - .watchPubkeys(asset, activateIfNeeded: true) - .listen( - (assetPubkeys) { - add(CoinAddressesPubkeysUpdated(assetPubkeys.keys)); - }, - onError: (Object err) { - add(CoinAddressesPubkeysSubscriptionFailed(err.toString())); - }, - ); + Future _startWatchingPubkeys(Asset asset) async { + try { + await _pubkeysSub?.cancel(); + _pubkeysSub = null; + // Pre-cache pubkeys to ensure that any newly created pubkeys are available + // when we start watching. UI flickering between old and new states is + // avoided this way. The watchPubkeys function yields the last known pubkeys + // when the pubkeys stream is first activated. + await sdk.pubkeys.precachePubkeys(asset); + _pubkeysSub = sdk.pubkeys + .watchPubkeys(asset, activateIfNeeded: true) + .listen( + (AssetPubkeys assetPubkeys) { + if (!isClosed) { + add(CoinAddressesPubkeysUpdated(assetPubkeys.keys)); + } + }, + onError: (Object err) { + if (!isClosed) { + add(CoinAddressesPubkeysSubscriptionFailed(err.toString())); + } + }, + ); + } catch (e) { + if (!isClosed) { + add(CoinAddressesPubkeysSubscriptionFailed(e.toString())); + } + } } @override diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart index a9b1825508..8b30a879cd 100644 --- a/lib/generated/codegen_loader.g.dart +++ b/lib/generated/codegen_loader.g.dart @@ -377,6 +377,9 @@ abstract class LocaleKeys { static const dexErrorMessage = 'dexErrorMessage'; static const seedConfirmInitialText = 'seedConfirmInitialText'; static const seedConfirmIncorrectText = 'seedConfirmIncorrectText'; + static const mnemonicInvalidWordError = 'mnemonicInvalidWordError'; + static const mnemonicInvalidChecksumError = 'mnemonicInvalidChecksumError'; + static const mnemonicInvalidLengthError = 'mnemonicInvalidLengthError'; static const usedSamePassword = 'usedSamePassword'; static const passwordNotAccepted = 'passwordNotAccepted'; static const confirmNewPassword = 'confirmNewPassword'; diff --git a/lib/views/wallets_manager/widgets/wallet_simple_import.dart b/lib/views/wallets_manager/widgets/wallet_simple_import.dart index 757ceebdae..ff71003452 100644 --- a/lib/views/wallets_manager/widgets/wallet_simple_import.dart +++ b/lib/views/wallets_manager/widgets/wallet_simple_import.dart @@ -31,12 +31,13 @@ class WalletSimpleImport extends StatefulWidget { required String name, required String password, required WalletConfig walletConfig, - }) onImport; + }) + onImport; final void Function() onCancel; final void Function({required String fileName, required String fileData}) - onUploadFiles; + onUploadFiles; @override State createState() => _WalletImportWrapperState(); @@ -340,14 +341,16 @@ class _WalletImportWrapperState extends State { return null; } - final maybeFailedReason = - context.read().mnemonicValidator.validateMnemonic( - seed ?? '', - minWordCount: 12, - maxWordCount: 24, - isHd: _isHdMode, - allowCustomSeed: _allowCustomSeed, - ); + final maybeFailedReason = context + .read() + .mnemonicValidator + .validateMnemonic( + seed ?? '', + minWordCount: 12, + maxWordCount: 24, + isHd: _isHdMode, + allowCustomSeed: _allowCustomSeed, + ); if (maybeFailedReason == null) { return null; @@ -356,18 +359,18 @@ class _WalletImportWrapperState extends State { return switch (maybeFailedReason) { MnemonicFailedReason.empty => LocaleKeys.walletCreationEmptySeedError.tr(), - MnemonicFailedReason.customNotSupportedForHd => _isHdMode - ? LocaleKeys.walletCreationHdBip39SeedError.tr() - : LocaleKeys.walletCreationBip39SeedError.tr(), - MnemonicFailedReason.customNotAllowed => - LocaleKeys.customSeedWarningText.tr(), - MnemonicFailedReason.invalidLength => - // TODO: Add this string has placeholders for min/max counts, which we - // specify as "12" and "24" - // LocaleKeys.seedPhraseCheckingEnterWord.tr(args: ['12', '24']), + MnemonicFailedReason.customNotSupportedForHd => _isHdMode ? LocaleKeys.walletCreationHdBip39SeedError.tr() : LocaleKeys.walletCreationBip39SeedError.tr(), + MnemonicFailedReason.customNotAllowed => + LocaleKeys.customSeedWarningText.tr(), + MnemonicFailedReason.invalidWord => + LocaleKeys.mnemonicInvalidWordError.tr(), + MnemonicFailedReason.invalidChecksum => + LocaleKeys.mnemonicInvalidChecksumError.tr(), + MnemonicFailedReason.invalidLength => + LocaleKeys.mnemonicInvalidLengthError.tr(args: ['12', '24']), }; } } diff --git a/packages/komodo_ui_kit/pubspec.lock b/packages/komodo_ui_kit/pubspec.lock index a85ea3306c..831aa6afd3 100644 --- a/packages/komodo_ui_kit/pubspec.lock +++ b/packages/komodo_ui_kit/pubspec.lock @@ -32,6 +32,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.19.1" + crypto: + dependency: transitive + description: + name: crypto + sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" + url: "https://pub.dev" + source: hosted + version: "3.0.6" decimal: dependency: transitive description: @@ -95,7 +103,7 @@ packages: description: path: "packages/komodo_defi_rpc_methods" ref: dev - resolved-ref: "68429b23dac43eddd53434dda1bd23296523f27d" + resolved-ref: d5b5c38da93e62f59bffb8e6b3e1e7ceb3fb91d5 url: "https://github.com/KomodoPlatform/komodo-defi-sdk-flutter.git" source: git version: "0.3.0+0" @@ -104,7 +112,7 @@ packages: description: path: "packages/komodo_defi_types" ref: dev - resolved-ref: "68429b23dac43eddd53434dda1bd23296523f27d" + resolved-ref: d5b5c38da93e62f59bffb8e6b3e1e7ceb3fb91d5 url: "https://github.com/KomodoPlatform/komodo-defi-sdk-flutter.git" source: git version: "0.3.0+0" @@ -113,7 +121,7 @@ packages: description: path: "packages/komodo_ui" ref: dev - resolved-ref: "68429b23dac43eddd53434dda1bd23296523f27d" + resolved-ref: d5b5c38da93e62f59bffb8e6b3e1e7ceb3fb91d5 url: "https://github.com/KomodoPlatform/komodo-defi-sdk-flutter.git" source: git version: "0.3.0+0" @@ -178,6 +186,14 @@ packages: description: flutter source: sdk version: "0.0.0" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" vector_math: dependency: transitive description: diff --git a/pubspec.lock b/pubspec.lock index 188b474b0f..cfe1ad4486 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -435,10 +435,10 @@ packages: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: f948e346c12f8d5480d2825e03de228d0eb8c3a737e4cdaa122267b89c022b5e + sha256: "6382ce712ff69b0f719640ce957559dde459e55ecd433c767e06d139ddf16cab" url: "https://pub.dev" source: hosted - version: "2.0.28" + version: "2.0.29" flutter_secure_storage: dependency: transitive description: @@ -554,10 +554,10 @@ packages: dependency: "direct main" description: name: get_it - sha256: e87cd1d108e472a0580348a543a0c49ed3d70c8a5c809c6d418583e595d0a389 + sha256: a4292e7cf67193f8e7c1258203104eb2a51ec8b3a04baa14695f4064c144297b url: "https://pub.dev" source: hosted - version: "8.1.0" + version: "8.2.0" glob: dependency: transitive description: @@ -656,7 +656,7 @@ packages: description: path: "packages/komodo_cex_market_data" ref: dev - resolved-ref: "68429b23dac43eddd53434dda1bd23296523f27d" + resolved-ref: d5b5c38da93e62f59bffb8e6b3e1e7ceb3fb91d5 url: "https://github.com/KomodoPlatform/komodo-defi-sdk-flutter.git" source: git version: "0.0.1" @@ -665,7 +665,7 @@ packages: description: path: "packages/komodo_coin_updates" ref: dev - resolved-ref: "68429b23dac43eddd53434dda1bd23296523f27d" + resolved-ref: d5b5c38da93e62f59bffb8e6b3e1e7ceb3fb91d5 url: "https://github.com/KomodoPlatform/komodo-defi-sdk-flutter.git" source: git version: "1.0.0" @@ -674,7 +674,7 @@ packages: description: path: "packages/komodo_coins" ref: dev - resolved-ref: "68429b23dac43eddd53434dda1bd23296523f27d" + resolved-ref: d5b5c38da93e62f59bffb8e6b3e1e7ceb3fb91d5 url: "https://github.com/KomodoPlatform/komodo-defi-sdk-flutter.git" source: git version: "0.3.0+0" @@ -683,7 +683,7 @@ packages: description: path: "packages/komodo_defi_framework" ref: dev - resolved-ref: "68429b23dac43eddd53434dda1bd23296523f27d" + resolved-ref: d5b5c38da93e62f59bffb8e6b3e1e7ceb3fb91d5 url: "https://github.com/KomodoPlatform/komodo-defi-sdk-flutter.git" source: git version: "0.3.0+0" @@ -692,7 +692,7 @@ packages: description: path: "packages/komodo_defi_local_auth" ref: dev - resolved-ref: "68429b23dac43eddd53434dda1bd23296523f27d" + resolved-ref: d5b5c38da93e62f59bffb8e6b3e1e7ceb3fb91d5 url: "https://github.com/KomodoPlatform/komodo-defi-sdk-flutter.git" source: git version: "0.3.0+0" @@ -701,7 +701,7 @@ packages: description: path: "packages/komodo_defi_rpc_methods" ref: dev - resolved-ref: "68429b23dac43eddd53434dda1bd23296523f27d" + resolved-ref: d5b5c38da93e62f59bffb8e6b3e1e7ceb3fb91d5 url: "https://github.com/KomodoPlatform/komodo-defi-sdk-flutter.git" source: git version: "0.3.0+0" @@ -710,7 +710,7 @@ packages: description: path: "packages/komodo_defi_sdk" ref: dev - resolved-ref: "68429b23dac43eddd53434dda1bd23296523f27d" + resolved-ref: d5b5c38da93e62f59bffb8e6b3e1e7ceb3fb91d5 url: "https://github.com/KomodoPlatform/komodo-defi-sdk-flutter.git" source: git version: "0.3.0+0" @@ -719,7 +719,7 @@ packages: description: path: "packages/komodo_defi_types" ref: dev - resolved-ref: "68429b23dac43eddd53434dda1bd23296523f27d" + resolved-ref: d5b5c38da93e62f59bffb8e6b3e1e7ceb3fb91d5 url: "https://github.com/KomodoPlatform/komodo-defi-sdk-flutter.git" source: git version: "0.3.0+0" @@ -735,7 +735,7 @@ packages: description: path: "packages/komodo_ui" ref: dev - resolved-ref: "68429b23dac43eddd53434dda1bd23296523f27d" + resolved-ref: d5b5c38da93e62f59bffb8e6b3e1e7ceb3fb91d5 url: "https://github.com/KomodoPlatform/komodo-defi-sdk-flutter.git" source: git version: "0.3.0+0" @@ -751,7 +751,7 @@ packages: description: path: "packages/komodo_wallet_build_transformer" ref: dev - resolved-ref: "68429b23dac43eddd53434dda1bd23296523f27d" + resolved-ref: d5b5c38da93e62f59bffb8e6b3e1e7ceb3fb91d5 url: "https://github.com/KomodoPlatform/komodo-defi-sdk-flutter.git" source: git version: "0.3.0+0" @@ -799,18 +799,18 @@ packages: dependency: transitive description: name: local_auth_android - sha256: "82b2bdeee2199a510d3b7716121e96a6609da86693bb0863edd8566355406b79" + sha256: "316503f6772dea9c0c038bb7aac4f68ab00112d707d258c770f7fc3c250a2d88" url: "https://pub.dev" source: hosted - version: "1.0.50" + version: "1.0.51" local_auth_darwin: dependency: transitive description: name: local_auth_darwin - sha256: "25163ce60a5a6c468cf7a0e3dc8a165f824cabc2aa9e39a5e9fc5c2311b7686f" + sha256: "0e9706a8543a4a2eee60346294d6a633dd7c3ee60fae6b752570457c4ff32055" url: "https://pub.dev" source: hosted - version: "1.5.0" + version: "1.6.0" local_auth_platform_interface: dependency: transitive description: @@ -1119,10 +1119,10 @@ packages: dependency: transitive description: name: shared_preferences_android - sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac" + sha256: "5bcf0772a761b04f8c6bf814721713de6f3e5d9d89caf8d3fe031b02a342379e" url: "https://pub.dev" source: hosted - version: "2.4.10" + version: "2.4.11" shared_preferences_foundation: dependency: transitive description: