Skip to content

Commit 3b38ed3

Browse files
OdrinEgor Komarov
and
Egor Komarov
authored
feat: mnemonic type (#802)
* chore: update `money2` & `money2_fixer` * feat: introduce seed phrase format and update mnemonic types * feat: allow to use a custom mnemonic for the 12 words seed phrase * fix: mnemonic type query parameter * fix: check balance when send message from dapp * refactor: remove unnecessary ignore comments * fix: align token transfer info widget content to the end * refactor: update token transfer info to use USD prices and improve currency fetching * chore: add error types to ignore in Sentry configuration * chore: enhance SentryWorker initialization with scope configuration * chore: update SentryWorker to include account information in scope configuration * fix: correct spelling of 'standard' in seed phrase format and related components * chore: refactor SentryWorker to use primary bus for scope configuration and improve initialization * feat: implement mnemonic type checking * fix: `onChangeTab` method --------- Co-authored-by: Egor Komarov <[email protected]>
1 parent ef336c2 commit 3b38ed3

File tree

62 files changed

+816
-2139
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+816
-2139
lines changed

assets/translations/en.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -642,5 +642,11 @@
642642
"errorMessageMaxUnconfirmedTransactions": "Unconfirmed transactions is limited by 5",
643643
"noRpcConnection": "No RPC connection",
644644
"tryAgainOrContact": "Please try again or contact technical support",
645-
"contactUs": "Contact us"
645+
"contactUs": "Contact us",
646+
"seedPhraseFormatLabel12": "Your seed pharse from:",
647+
"seedPhraseFormatLabel24": "Seed phrase format:",
648+
"seedPhrase12FormatStandart": "SparX wallet",
649+
"seedPhrase12FormatTON": "TON types wallet",
650+
"seedPhrase24FormatStandart": "TON Standard",
651+
"seedPhrase24FormatTON": "TON BIP39"
646652
}

assets/translations/ko.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -642,5 +642,11 @@
642642
"errorMessageMaxUnconfirmedTransactions": "Unconfirmed transactions is limited by 5",
643643
"noRpcConnection": "No RPC connection",
644644
"tryAgainOrContact": "Please try again or contact technical support",
645-
"contactUs": "Contact us"
645+
"contactUs": "Contact us",
646+
"seedPhraseFormatLabel12": "Your seed pharse from:",
647+
"seedPhraseFormatLabel24": "Seed phrase format:",
648+
"seedPhrase12FormatStandart": "SparX wallet",
649+
"seedPhrase12FormatTON": "TON types wallet",
650+
"seedPhrase24FormatStandart": "TON Standard",
651+
"seedPhrase24FormatTON": "TON BIP39"
646652
}

lib/app/router/routs/add_seed/add_seed.dart

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1+
import 'dart:convert';
2+
13
import 'package:app/app/router/router.dart';
24
import 'package:app/data/models/seed/seed_phrase_model.dart';
35
import 'package:app/feature/add_seed/add_existing_wallet/view/add_existing_wallet_page.dart';
46
import 'package:app/feature/add_seed/add_seed_enable_biometry/add_seed_enable_biometry_page.dart';
57
import 'package:app/feature/add_seed/create_password/screens/create_seed_password/create_seed_password_screen.dart';
68
import 'package:app/feature/add_seed/create_password/view/create_seed_password_page.dart';
7-
import 'package:app/feature/add_seed/enter_seed_phrase/enter_seed_phrase.dart';
9+
import 'package:app/feature/add_seed/enter_seed_phrase/enter_seed_phrase_widget.dart';
810
import 'package:app/feature/add_seed/import_wallet/import_wallet_screen.dart';
11+
import 'package:app/utils/utils.dart';
912
import 'package:app/v1/feature/add_seed/check_seed_phrase/check_seed_phrase.dart';
1013
import 'package:app/v1/feature/add_seed/create_seed/create_seed.dart';
1114
import 'package:app/v1/feature/add_seed/enter_seed_name/view/enter_seed_name_page.dart';
@@ -15,6 +18,8 @@ import 'package:nekoton_repository/nekoton_repository.dart';
1518
/// Name for phrase from queryParams to create or import seed.
1619
const addSeedPhraseQueryParam = 'addSeedPhrase';
1720

21+
const mnemonicTypeQueryParam = 'mnemonicType';
22+
1823
/// Name of path field for navigation
1924
const enterSeedNameCommandPathParam = 'command';
2025

@@ -54,7 +59,7 @@ GoRoute createSeedNoNamedRoute(GoRoute passwordRoute) {
5459
GoRoute enterSeedNoNamedRoute(GoRoute passwordRoute) {
5560
return GoRoute(
5661
path: AppRoute.enterSeed.path,
57-
builder: (_, __) => const EnterSeedPhrase(),
62+
builder: (_, __) => const EnterSeedPhraseWidget(),
5863
routes: [
5964
passwordRoute,
6065
],
@@ -97,6 +102,11 @@ GoRoute get createOnboardingSeedPasswordRoute {
97102
phrase: SeedPhraseModel(
98103
state.uri.queryParameters[addSeedPhraseQueryParam],
99104
),
105+
mnemonicType: state.uri.queryParameters[mnemonicTypeQueryParam]?.let(
106+
(it) => const MnemonicTypeJsonConverter().fromJson(
107+
jsonDecode(it) as Map<String, dynamic>,
108+
),
109+
),
100110
);
101111
},
102112
routes: [
@@ -124,6 +134,11 @@ GoRoute get createSeedNoNamedProfileRoute {
124134
),
125135
name: state.pathParameters[enterSeedNameNamePathParam],
126136
type: SeedAddType.create,
137+
mnemonicType: state.uri.queryParameters[mnemonicTypeQueryParam]?.let(
138+
(it) => const MnemonicTypeJsonConverter().fromJson(
139+
jsonDecode(it) as Map<String, dynamic>,
140+
),
141+
),
127142
),
128143
),
129144
);
@@ -140,6 +155,11 @@ GoRoute get enterSeedNoNamedProfileRoute {
140155
),
141156
name: state.pathParameters[enterSeedNameNamePathParam],
142157
type: SeedAddType.import,
158+
mnemonicType: state.uri.queryParameters[mnemonicTypeQueryParam]?.let(
159+
(it) => const MnemonicTypeJsonConverter().fromJson(
160+
jsonDecode(it) as Map<String, dynamic>,
161+
),
162+
),
143163
),
144164
),
145165
);
@@ -157,6 +177,11 @@ GoRoute get createSeedNamedProfileRoute {
157177
),
158178
name: state.pathParameters[enterSeedNameNamePathParam],
159179
type: SeedAddType.create,
180+
mnemonicType: state.uri.queryParameters[mnemonicTypeQueryParam]?.let(
181+
(it) => const MnemonicTypeJsonConverter().fromJson(
182+
jsonDecode(it) as Map<String, dynamic>,
183+
),
184+
),
160185
),
161186
);
162187

@@ -186,7 +211,7 @@ GoRoute get createSeedNamedProfileRoute {
186211
GoRoute get enterSeedNamedProfileRoute {
187212
return GoRoute(
188213
path: AppRoute.enterSeedNamed.path,
189-
builder: (_, state) => const EnterSeedPhrase(),
214+
builder: (_, state) => const EnterSeedPhraseWidget(),
190215
routes: [
191216
GoRoute(
192217
path: AppRoute.createSeedPassword.path,
@@ -197,6 +222,12 @@ GoRoute get enterSeedNamedProfileRoute {
197222
),
198223
name: state.pathParameters[enterSeedNameNamePathParam],
199224
type: SeedAddType.import,
225+
mnemonicType:
226+
state.uri.queryParameters[mnemonicTypeQueryParam]?.let(
227+
(it) => const MnemonicTypeJsonConverter().fromJson(
228+
jsonDecode(it) as Map<String, dynamic>,
229+
),
230+
),
200231
);
201232
},
202233
),

lib/app/service/storage_service/balance_storage_service.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ class BalanceStorageService extends AbstractStorageService {
7676
.map(
7777
(entry) => (
7878
_Key.fromString(entry.key),
79-
FixedFixer.fromJson(entry.value as Map<String, dynamic>),
79+
FixedFixer.fromJsonImproved(entry.value as Map<String, dynamic>),
8080
),
8181
)
8282
.groupFoldBy<NetworkGroup, Map<Address, Fixed>>(
@@ -94,7 +94,7 @@ class BalanceStorageService extends AbstractStorageService {
9494
try {
9595
_overallBalancesStorage.write(
9696
_Key(group: group, address: accountAddress).toString(),
97-
balance.toJson(),
97+
balance.toJsonImproved(),
9898
);
9999
_streamedOverallBalance();
100100
} catch (e, t) {

lib/bootstrap/sentry.dart

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
1+
import 'dart:async';
2+
3+
import 'package:app/app/service/service.dart';
14
import 'package:app/core/app_build_type.dart';
2-
import 'package:app/utils/define_env.dart';
5+
import 'package:app/event_bus/events/bootstrap/bootstrap_event.dart';
6+
import 'package:app/event_bus/primary_bus.dart';
7+
import 'package:app/utils/utils.dart';
8+
import 'package:collection/collection.dart';
39
import 'package:logging/logging.dart';
10+
import 'package:nekoton_repository/nekoton_repository.dart';
11+
import 'package:rxdart/rxdart.dart';
412
import 'package:sentry_flutter/sentry_flutter.dart';
513

614
class SentryWorker {
@@ -18,12 +26,20 @@ class SentryWorker {
1826
final _logger = Logger('Sentry');
1927

2028
AppBuildType? _appBuildType;
29+
NekotonRepository? _nekotonRepository;
30+
GeneralStorageService? _generalStorageService;
2131

2232
/// If dev build type - don't use sentry
2333
bool get _isUseSentry => _appBuildType != AppBuildType.development;
2434

25-
Future<void> init(AppBuildType appBuildType) async {
35+
Future<void> init({
36+
required AppBuildType appBuildType,
37+
required NekotonRepository nekotonRepository,
38+
required GeneralStorageService generalStorageService,
39+
}) async {
2640
_appBuildType = appBuildType;
41+
_nekotonRepository = nekotonRepository;
42+
_generalStorageService = generalStorageService;
2743

2844
if (!_isUseSentry) {
2945
_logger.info(
@@ -32,11 +48,21 @@ class SentryWorker {
3248
return;
3349
}
3450

51+
unawaited(
52+
primaryBus.on<BootstrapCompleteEvent>().first.then(
53+
(_) => _configureScope(),
54+
),
55+
);
56+
3557
return SentryFlutter.init(
3658
(options) {
3759
options
3860
..dsn = dsnDefineEnv
39-
..tracesSampleRate = 1;
61+
..tracesSampleRate = 1
62+
..ignoreErrors = [
63+
'AnyhowException(Account not exists)',
64+
'AnyhowException(Network error)',
65+
];
4066
},
4167
);
4268
}
@@ -50,4 +76,33 @@ class SentryWorker {
5076
}
5177
Sentry.captureException(exception, stackTrace: stackTrace);
5278
}
79+
80+
void _configureScope() {
81+
if (_nekotonRepository == null || _generalStorageService == null) return;
82+
83+
Rx.combineLatest3(
84+
_nekotonRepository!.currentTransportStream,
85+
_nekotonRepository!.accountsStorage.accountsStream,
86+
_generalStorageService!.currentAddressStream,
87+
(transport, accounts, address) => (transport, accounts, address),
88+
).map((event) {
89+
final (transport, accounts, address) = event;
90+
final account = accounts.firstWhereOrNull((it) => it.address == address);
91+
return (transport, account);
92+
}).listen((event) {
93+
final (transport, account) = event;
94+
Sentry.configureScope((scope) {
95+
scope
96+
..setUser(
97+
account?.let(
98+
(it) => SentryUser(
99+
id: it.address.toString(),
100+
data: it.toJson(),
101+
),
102+
),
103+
)
104+
..setContexts('network', transport.networkName);
105+
});
106+
});
107+
}
53108
}

lib/data/models/models.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,12 @@ export 'key_removed_event.dart';
1414
export 'pending_transaction_with_additional_info.dart';
1515
export 'permission.dart';
1616
export 'permissions.dart';
17+
export 'seed/seed_phrase_format.dart';
18+
export 'seed/seed_phrase_model.dart';
1719
export 'signed_message_with_additional_info.dart';
1820
export 'site_meta_data.dart';
19-
export 'stever/stever.dart';
21+
export 'stever/st_ever_details.dart';
22+
export 'stever/stever_withdraw_request.dart';
2023
export 'token_balance.dart';
2124
export 'token_contract/token_contract_asset.dart';
2225
export 'token_wallet_ordinary_transaction.dart';
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
enum SeedPhraseFormat {
2+
/// Used by Sparx Wallet & Ever Wallet
3+
standard,
4+
5+
/// Used by some TON wallets
6+
ton,
7+
}

lib/data/models/stever/stever.dart

Lines changed: 0 additions & 2 deletions
This file was deleted.

lib/feature/add_seed/add_seed.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export 'add_existing_wallet/add_existing_wallet.dart';
2+
export 'add_seed_enable_biometry/add_seed_enable_biometry.dart';
3+
export 'create_password/create_password.dart';
4+
export 'enter_seed_phrase/enter_seed_phrase.dart';
5+
export 'import_wallet/import_wallet.dart';
6+
export 'view/view.dart';
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export 'add_seed_enable_biometry_page.dart';
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
export 'cubit/cubit.dart';
2+
export 'model/model.dart';
3+
export 'screens/screens.dart';
24
export 'view/view.dart';
5+
export 'widgets/widgets.dart';

lib/feature/add_seed/create_password/cubit/create_seed_password_cubit.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class CreateSeedPasswordCubit extends Cubit<CreateSeedPasswordState>
2626
required this.completeCallback,
2727
required this.seedPhrase,
2828
required this.type,
29+
required this.mnemonicType,
2930
this.setCurrentKey = false,
3031
this.name,
3132
}) : super(CreateSeedPasswordState.initial()) {
@@ -45,6 +46,7 @@ class CreateSeedPasswordCubit extends Cubit<CreateSeedPasswordState>
4546
/// If true, then current key will be set as default (provided in onboarding)
4647
final bool setCurrentKey;
4748
final SeedAddType type;
49+
final MnemonicType? mnemonicType;
4850

4951
final passwordController = TextEditingController();
5052
final confirmController = TextEditingController();
@@ -86,6 +88,7 @@ class CreateSeedPasswordCubit extends Cubit<CreateSeedPasswordState>
8688
password: passwordController.text,
8789
name: name,
8890
addType: type,
91+
mnemonicType: mnemonicType,
8992
);
9093
if (setCurrentKey) {
9194
currentKeyService.changeCurrentKey(publicKey);
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
//GENERATED BARREL FILE
21
export 'create_seed_password_cubit.dart';
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export 'password_status.dart';
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export 'create_seed_password_screen.dart';
2+
export 'create_seed_password_screen_model.dart';
3+
export 'create_seed_password_screen_wm.dart';

lib/feature/add_seed/create_password/screens/create_seed_password/create_seed_password_screen.dart

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,24 @@ import 'package:app/utils/focus_utils.dart';
66
import 'package:elementary/elementary.dart';
77
import 'package:elementary_helper/elementary_helper.dart';
88
import 'package:flutter/material.dart';
9+
import 'package:nekoton_repository/nekoton_repository.dart';
910
import 'package:ui_components_lib/ui_components_lib.dart';
1011

1112
/// {@template create_seed_password_onboarding_page}
1213
/// Entry point to create seed password from onboarding.
1314
/// {@endtemplate}
1415
class CreateSeedPasswordScreen
1516
extends ElementaryWidget<CreateSeedPasswordScreenWidgetModel> {
16-
CreateSeedPasswordScreen({
17+
const CreateSeedPasswordScreen({
18+
required this.phrase,
19+
required this.mnemonicType,
1720
Key? key,
18-
WidgetModelFactory<CreateSeedPasswordScreenWidgetModel>? wmFactory,
19-
SeedPhraseModel? phrase,
20-
}) : super(
21-
wmFactory ??
22-
(context) => defaultCreateSeedPasswordScreenWidgetModelFactory(
23-
context,
24-
phrase: phrase,
25-
),
26-
key: key,
27-
);
21+
WidgetModelFactory wmFactory =
22+
defaultCreateSeedPasswordScreenWidgetModelFactory,
23+
}) : super(wmFactory, key: key);
24+
25+
final SeedPhraseModel? phrase;
26+
final MnemonicType? mnemonicType;
2827

2928
@override
3029
Widget build(CreateSeedPasswordScreenWidgetModel wm) {

lib/feature/add_seed/create_password/screens/create_seed_password/create_seed_password_screen_model.dart

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,35 +18,36 @@ class CreateSeedPasswordScreenModel extends ElementaryModel {
1818
this._currentAccountService,
1919
this._messengerService,
2020
this._nekotonRepository,
21-
this._phrase,
2221
) : super(errorHandler: errorHandler);
2322

2423
final BiometryService _biometryService;
2524
final CurrentKeyService _currentKeyService;
2625
final CurrentAccountsService _currentAccountService;
2726
final MessengerService _messengerService;
2827
final NekotonRepository _nekotonRepository;
29-
final SeedPhraseModel? _phrase;
3028

3129
bool get isNeedBiometry =>
3230
_biometryService.isAvailable && !_biometryService.isEnabled;
3331

3432
Future<void> next({
3533
required BuildContext context,
3634
required String password,
35+
required SeedPhraseModel? phrase,
36+
required MnemonicType? mnemonicType,
3737
}) async {
3838
late SeedPhraseModel seed;
3939

4040
try {
41-
if (_phrase?.isNotEmpty ?? false) {
42-
seed = _phrase!;
41+
if (phrase?.isNotEmpty ?? false) {
42+
seed = phrase!;
4343
} else {
4444
seed = _createSeed();
4545
}
4646

4747
final publicKey = await _nekotonRepository.addSeed(
4848
phrase: seed.words,
4949
password: password,
50+
mnemonicType: mnemonicType,
5051
);
5152

5253
_currentKeyService.changeCurrentKey(publicKey);

0 commit comments

Comments
 (0)