Skip to content

Commit 50c3b88

Browse files
authored
Merge pull request #246 from broxus/feature/EWM-397_new_defaul_naming
feat(EWM-397): new default naming logic for seed and account
2 parents 40456d3 + d2cdbfd commit 50c3b88

File tree

8 files changed

+720
-14
lines changed

8 files changed

+720
-14
lines changed

CHANGELOG.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,33 @@
33
All notable changes to this project will be documented in this file.
44
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
55

6+
## 2025-10-22
7+
8+
### Changes
9+
10+
---
11+
12+
Packages with breaking changes:
13+
14+
- There are no breaking changes in this release.
15+
16+
Packages with other changes:
17+
18+
- [`nekoton_repository` - `v2.0.3-dev.0`](#nekoton_repository---v203-dev0)
19+
20+
---
21+
22+
#### `nekoton_repository` - `v2.0.3-dev.0`
23+
24+
- **FIX**(EWM-397): fix tests & create tests for new naming logic. ([81c66a2d](https://github.com/broxus/nekoton_repository/commit/81c66a2dd6bbf2e0e78477af910cac045b910205))
25+
- **FIX**(EWM-397): formatting. ([5a26933b](https://github.com/broxus/nekoton_repository/commit/5a26933bb70e622259d2d71196a95430df7604ae))
26+
27+
## 2.0.3-dev.0
28+
29+
- **FIX**(EWM-397): fix tests & create tests for new naming logic. ([81c66a2d](https://github.com/broxus/nekoton_repository/commit/81c66a2dd6bbf2e0e78477af910cac045b910205))
30+
- **FIX**(EWM-397): formatting. ([5a26933b](https://github.com/broxus/nekoton_repository/commit/5a26933bb70e622259d2d71196a95430df7604ae))
31+
32+
633
## 2025-10-20
734

835
### Changes

lib/src/models/account_list.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ class AccountList extends Equatable {
3636

3737
/// Add account to key with [publicKey] and [walletType].
3838
/// [workchain] specify Transport network that should be used for this account
39-
/// [name] is optional and if not specified, auto-generated name will be used.
39+
/// [name] is optional and if not specified, auto-generated name will be used
40+
/// in format "Account N.M".
4041
Future<Address> addAccount({
4142
required WalletType walletType,
4243
required int workchain,
@@ -46,6 +47,9 @@ class AccountList extends Equatable {
4647
AccountToAdd(
4748
name:
4849
name ??
50+
GetIt.instance<NekotonRepository>().generateDefaultAccountName(
51+
publicKey,
52+
) ??
4953
GetIt.instance<TransportRepository>().currentTransport
5054
.defaultAccountName(walletType),
5155
publicKey: publicKey,

lib/src/nekoton_repository.dart

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,61 @@ class NekotonRepository
466466
_keyStore.keysStream.listen((keys) => _hasSeeds.add(keys.isNotEmpty));
467467
}
468468

469+
/// Generates default account name in format "Account N.M"
470+
/// where N is the key position in the seed (1-based) and M is the account
471+
/// position within that key (1-based).
472+
///
473+
/// Returns null if the publicKey is not found in any seed.
474+
String? generateDefaultAccountName(PublicKey publicKey) {
475+
final seed = seedList.findSeedByAnyPublicKey(publicKey);
476+
if (seed == null) return null;
477+
478+
// Find the key within the seed
479+
final keyIndex = seed.allKeys.indexWhere(
480+
(key) => key.publicKey == publicKey,
481+
);
482+
if (keyIndex == -1) return null;
483+
484+
// Key position is 1-based (master key = 1, first derived = 2, etc.)
485+
final keyPosition = keyIndex + 1;
486+
487+
// Get next account number by finding max existing number + 1
488+
final accountPosition = _getNextAccountNumber(
489+
seed.allKeys[keyIndex].accountList.allAccounts,
490+
keyPosition,
491+
);
492+
493+
return 'Account $keyPosition.$accountPosition';
494+
}
495+
496+
/// Gets the next available account number for a specific key position
497+
/// by finding the maximum number in existing account names
498+
/// (format: "Account N.M") and returning max M + 1 for the given N.
499+
/// Returns 1 if no accounts exist or no default names are found.
500+
int _getNextAccountNumber(List<KeyAccount> accounts, int keyPosition) {
501+
if (accounts.isEmpty) return 1;
502+
503+
var maxNumber = 0;
504+
505+
for (final account in accounts) {
506+
final name = account.name;
507+
508+
// Parse "Account N.M" format
509+
final match = RegExp(r'^Account (\d+)\.(\d+)$').firstMatch(name);
510+
if (match != null) {
511+
final n = int.tryParse(match.group(1) ?? '');
512+
final m = int.tryParse(match.group(2) ?? '');
513+
514+
// Only consider accounts for this specific key position
515+
if (n == keyPosition && m != null && m > maxNumber) {
516+
maxNumber = m;
517+
}
518+
}
519+
}
520+
521+
return maxNumber + 1;
522+
}
523+
469524
void _logHandler(fnb.LogEntry logEntry) {
470525
final logLevel = _toLogLevel(logEntry.level);
471526

lib/src/repositories/account_repository/account_repository_impl.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'package:collection/collection.dart';
2+
import 'package:get_it/get_it.dart';
23
import 'package:nekoton_repository/nekoton_repository.dart';
34

45
/// Implementation of AccountRepository.
@@ -63,6 +64,9 @@ mixin AccountRepositoryImpl on TransportRepository
6364
AccountToAdd(
6465
name:
6566
name ??
67+
GetIt.instance<NekotonRepository>().generateDefaultAccountName(
68+
publicKey,
69+
) ??
6670
currentTransport.defaultAccountName(
6771
existingWalletInfo.walletType,
6872
),

lib/src/repositories/seed_repository/seed_repository_impl.dart

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import 'package:get_it/get_it.dart';
44
import 'package:nekoton_repository/nekoton_repository.dart';
55
import 'package:rxdart/rxdart.dart';
66

7+
const seedPrefix = 'Seed ';
8+
79
/// Implementation of SeedRepository.
810
/// Usage
911
/// ```
@@ -159,7 +161,11 @@ mixin SeedKeyRepositoryImpl implements SeedKeyRepository {
159161
String? name,
160162
SeedAddType addType = SeedAddType.create,
161163
}) async {
162-
name = name?.isEmpty ?? true ? null : name;
164+
// Generate default seed name if not provided
165+
if (name?.isEmpty ?? true) {
166+
name = '$seedPrefix${_getNextSeedNumber()}';
167+
}
168+
163169
mnemonicType ??= phrase.length == 24
164170
? const MnemonicType.legacy()
165171
: const MnemonicType.bip39(
@@ -239,6 +245,11 @@ mixin SeedKeyRepositoryImpl implements SeedKeyRepository {
239245
required int workchainId,
240246
String? name,
241247
}) async {
248+
// Generate default seed name if not provided
249+
if (name?.isEmpty ?? true) {
250+
name = '$seedPrefix${_getNextSeedNumber()}';
251+
}
252+
242253
final publicKey = await keyStore.addKey(
243254
LedgerKeyCreateInput(accountId: accountId, name: name),
244255
);
@@ -266,8 +277,16 @@ mixin SeedKeyRepositoryImpl implements SeedKeyRepository {
266277
final transport = currentTransport;
267278
if (transport.transport.disposed) return;
268279

280+
// For newly created seeds, this will be the first account on master key
281+
// At this point the seed might not be in seedList yet, so we use fallback
282+
final accountName =
283+
GetIt.instance<NekotonRepository>().generateDefaultAccountName(
284+
publicKey,
285+
) ??
286+
transport.defaultAccountName(transport.defaultWalletType);
287+
269288
final defaultAccount = AccountToAdd(
270-
name: transport.defaultAccountName(transport.defaultWalletType),
289+
name: accountName,
271290
publicKey: publicKey,
272291
contract: transport.defaultWalletType,
273292
workchain: workchainId,
@@ -356,7 +375,10 @@ mixin SeedKeyRepositoryImpl implements SeedKeyRepository {
356375
publicKey: a.publicKey,
357376
contract: a.walletType,
358377
workchain: a.address.workchain,
359-
name: transport.defaultAccountName(a.walletType),
378+
name:
379+
GetIt.instance<NekotonRepository>()
380+
.generateDefaultAccountName(a.publicKey) ??
381+
transport.defaultAccountName(a.walletType),
360382
),
361383
),
362384
);
@@ -434,7 +456,10 @@ mixin SeedKeyRepositoryImpl implements SeedKeyRepository {
434456
if (activeAccounts.isEmpty) {
435457
accountsToAdd.add(
436458
AccountToAdd(
437-
name: transport.defaultAccountName(transport.defaultWalletType),
459+
name:
460+
GetIt.instance<NekotonRepository>()
461+
.generateDefaultAccountName(key) ??
462+
transport.defaultAccountName(transport.defaultWalletType),
438463
publicKey: key,
439464
contract: transport.defaultWalletType,
440465
workchain: workchainId,
@@ -451,7 +476,10 @@ mixin SeedKeyRepositoryImpl implements SeedKeyRepository {
451476
publicKey: a.publicKey,
452477
contract: a.walletType,
453478
workchain: a.address.workchain,
454-
name: transport.defaultAccountName(a.walletType),
479+
name:
480+
GetIt.instance<NekotonRepository>()
481+
.generateDefaultAccountName(a.publicKey) ??
482+
transport.defaultAccountName(a.walletType),
455483
),
456484
),
457485
);
@@ -659,4 +687,30 @@ mixin SeedKeyRepositoryImpl implements SeedKeyRepository {
659687
}
660688
await GetIt.instance<AccountRepository>().removeAccounts(accountsToRemove);
661689
}
690+
691+
/// Gets the next available seed number by finding the maximum number
692+
/// in existing seed names (format: "Seed N") and returning max + 1.
693+
/// Returns 1 if no seeds exist or no default names are found.
694+
int _getNextSeedNumber() {
695+
final seedMeta = storageRepository.seedMeta;
696+
if (seedMeta.isEmpty) return 1;
697+
698+
var maxNumber = 0;
699+
700+
for (final metadata in seedMeta.values) {
701+
final name = metadata.name;
702+
if (name == null) continue;
703+
704+
// Parse "Seed " format
705+
final match = RegExp('^$seedPrefix(\\d+)\$').firstMatch(name);
706+
if (match != null) {
707+
final number = int.tryParse(match.group(1) ?? '');
708+
if (number != null && number > maxNumber) {
709+
maxNumber = number;
710+
}
711+
}
712+
}
713+
714+
return maxNumber + 1;
715+
}
662716
}

pubspec.yaml

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
name: nekoton_repository
33
description: Nekoton repository package
4-
version: 2.0.1
4+
version: 2.0.3-dev.0
55
repository: https://github.com/broxus/nekoton_repository
66

77
environment:
@@ -49,8 +49,7 @@ flutter_gen:
4949
package_parameter_enabled: true
5050

5151
flutter:
52-
assets:
53-
- assets/abi/
52+
assets: [assets/abi/]
5453

5554
melos:
5655
useRootAsPackage: true
@@ -61,10 +60,12 @@ melos:
6160

6261
codegen:
6362
description: Generate code for all packages
64-
exec: "find . -type f -name \"*.gen.dart\" -delete && flutter packages pub run build_runner build --delete-conflicting-outputs && dart format lib/generated/assets.gen.dart lib/nekoton_repository.module.dart"
63+
exec: find . -type f -name "*.gen.dart" -delete && flutter packages pub run
64+
build_runner build --delete-conflicting-outputs && dart format lib/generated/assets.gen.dart
65+
lib/nekoton_repository.module.dart
6566
failFast: true
6667
packageFilters:
67-
dependsOn: "build_runner"
68+
dependsOn: build_runner
6869

6970
analyze:
7071
description: Analyze a specific package in this project.
@@ -82,15 +83,16 @@ melos:
8283

8384
test:
8485
description: Run Flutter tests for a specific package in this project.
85-
exec: "flutter test test"
86+
exec: flutter test test
8687
failFast: true
8788
packageFilters:
8889
flutter: true
8990
dirExists: test
9091

9192
test:integration:
9293
run: melos exec -c 1 --fail-fast -- "flutter test integration_test"
93-
description: Run Flutter teintegration teststs for a specific package in this project.
94+
description: Run Flutter teintegration teststs for a specific package in this
95+
project.
9496
packageFilters:
9597
flutter: true
9698
dirExists: integration_test
@@ -112,4 +114,3 @@ melos:
112114
version:
113115
hooks:
114116
preCommit: melos bs && git add --all
115-

0 commit comments

Comments
 (0)