Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions lib/bloc/auth_bloc/auth_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class AuthBloc extends Bloc<AuthBlocEvent, AuthBlocState> with TrezorAuthMixin {
/// Handles [AuthBlocEvent]s and emits [AuthBlocState]s.
/// [_kdfSdk] is an instance of [KomodoDefiSdk] used for authentication.
AuthBloc(this._kdfSdk, this._walletsRepository, this._settingsRepository)
: super(AuthBlocState.initial()) {
: super(AuthBlocState.initial()) {
on<AuthModeChanged>(_onAuthChanged);
on<AuthStateClearRequested>(_onClearState);
on<AuthSignOutRequested>(_onLogout);
Expand Down Expand Up @@ -171,7 +171,6 @@ class AuthBloc extends Bloc<AuthBlocEvent, AuthBlocState> with TrezorAuthMixin {
);

_log.info('registered from a wallet');
await _kdfSdk.setWalletType(event.wallet.config.type);
await _kdfSdk.confirmSeedBackup(hasBackup: false);
await _kdfSdk.addActivatedCoins(enabledByDefaultCoins);

Expand Down Expand Up @@ -220,7 +219,6 @@ class AuthBloc extends Bloc<AuthBlocEvent, AuthBlocState> with TrezorAuthMixin {
);

_log.info('restored from a wallet');
await _kdfSdk.setWalletType(event.wallet.config.type);
await _kdfSdk.confirmSeedBackup(hasBackup: event.wallet.config.hasBackup);
await _kdfSdk.addActivatedCoins(enabledByDefaultCoins);

Expand Down Expand Up @@ -342,8 +340,9 @@ class AuthBloc extends Bloc<AuthBlocEvent, AuthBlocState> with TrezorAuthMixin {
void _listenToAuthStateChanges() {
_authChangesSubscription?.cancel();
_authChangesSubscription = _kdfSdk.auth.watchCurrentUser().listen((user) {
final AuthorizeMode event =
user != null ? AuthorizeMode.logIn : AuthorizeMode.noLogin;
final AuthorizeMode event = user != null
? AuthorizeMode.logIn
: AuthorizeMode.noLogin;
add(AuthModeChanged(mode: event, currentUser: user));
});
}
Expand Down
1 change: 0 additions & 1 deletion lib/bloc/auth_bloc/trezor_auth_mixin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,6 @@ mixin TrezorAuthMixin on Bloc<AuthBlocEvent, AuthBlocState> {
);
}

await _sdk.setWalletType(WalletType.trezor);
await _sdk.confirmSeedBackup(hasBackup: true);
if (authState.user!.wallet.config.activatedCoins.isEmpty) {
// If no coins are activated, we assume this is the first time
Expand Down
66 changes: 29 additions & 37 deletions lib/bloc/coins_bloc/coins_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,7 @@ part 'coins_state.dart';

/// Responsible for coin activation, deactivation, syncing, and fiat price
class CoinsBloc extends Bloc<CoinsEvent, CoinsState> {
CoinsBloc(
this._kdfSdk,
this._coinsRepo,
) : super(CoinsState.initial()) {
CoinsBloc(this._kdfSdk, this._coinsRepo) : super(CoinsState.initial()) {
on<CoinsStarted>(_onCoinsStarted, transformer: droppable());
// TODO: move auth listener to ui layer: bloclistener should fire auth events
on<CoinsBalanceMonitoringStarted>(_onCoinsBalanceMonitoringStarted);
Expand Down Expand Up @@ -75,14 +72,7 @@ class CoinsBloc extends Bloc<CoinsEvent, CoinsState> {
final pubkeys = await _kdfSdk.pubkeys.getPubkeys(asset);

// Update state with new pubkeys
emit(
state.copyWith(
pubkeys: {
...state.pubkeys,
event.coinId: pubkeys,
},
),
);
emit(state.copyWith(pubkeys: {...state.pubkeys, event.coinId: pubkeys}));
} catch (e, s) {
_log.shout('Failed to get pubkeys for ${event.coinId}', e, s);
}
Expand Down Expand Up @@ -201,14 +191,16 @@ class CoinsBloc extends Bloc<CoinsEvent, CoinsState> {
emit(_prePopulateListWithActivatingCoins(event.coinIds));
await _activateCoins(event.coinIds, emit);

final currentWallet = await _kdfSdk.currentWallet();
if (currentWallet?.config.type == WalletType.iguana ||
currentWallet?.config.type == WalletType.hdwallet) {
final currentUser = await _kdfSdk.auth.currentUser;
final derivation = currentUser?.walletId.authOptions.derivationMethod;
if (derivation == DerivationMethod.iguana ||
derivation == DerivationMethod.hdWallet) {
final coinUpdates = _syncIguanaCoinsStates();
await emit.forEach(
coinUpdates,
onData: (coin) => state
.copyWith(walletCoins: {...state.walletCoins, coin.id.id: coin}),
onData: (coin) => state.copyWith(
walletCoins: {...state.walletCoins, coin.id.id: coin},
),
);
}

Expand Down Expand Up @@ -261,8 +253,9 @@ class CoinsBloc extends Bloc<CoinsEvent, CoinsState> {
Map<String, Coin> currentCoins,
) {
final updatedWalletCoins = Map.fromEntries(
currentWalletCoins.entries
.where((entry) => !coinsToDisable.contains(entry.key)),
currentWalletCoins.entries.where(
(entry) => !coinsToDisable.contains(entry.key),
),
);
final updatedCoins = Map<String, Coin>.of(currentCoins);
for (final assetId in coinsToDisable) {
Expand Down Expand Up @@ -337,11 +330,7 @@ class CoinsBloc extends Bloc<CoinsEvent, CoinsState> {
) async {
add(CoinsBalanceMonitoringStopped());

emit(
state.copyWith(
walletCoins: {},
),
);
emit(state.copyWith(walletCoins: {}));
_coinsRepo.flushCache();
}

Expand Down Expand Up @@ -376,12 +365,10 @@ class CoinsBloc extends Bloc<CoinsEvent, CoinsState> {
final knownCoins = _coinsRepo.getKnownCoinsMap();
final activatingCoins = Map<String, Coin>.fromIterable(
coins
.map(
(coin) {
final sdkCoin = knownCoins[coin];
return sdkCoin?.copyWith(state: CoinState.activating);
},
)
.map((coin) {
final sdkCoin = knownCoins[coin];
return sdkCoin?.copyWith(state: CoinState.activating);
})
.where((coin) => coin != null)
.cast<Coin>(),
key: (element) => (element as Coin).id.id,
Expand All @@ -398,8 +385,7 @@ class CoinsBloc extends Bloc<CoinsEvent, CoinsState> {
/// When multiple coins are found for the provided IDs,
Stream<Coin> _syncIguanaCoinsStates() async* {
final coinsBlocWalletCoinsState = state.walletCoins;
final previouslyActivatedCoinIds =
(await _kdfSdk.currentWallet())?.config.activatedCoins ?? [];
final previouslyActivatedCoinIds = (await _kdfSdk.getWalletCoinIds());

final walletAssets = <Asset>[];
for (final coinId in previouslyActivatedCoinIds) {
Expand All @@ -413,17 +399,21 @@ class CoinsBloc extends Bloc<CoinsEvent, CoinsState> {
}
if (assets.length > 1) {
final assetIds = assets.map((a) => a.id.id).join(', ');
_log.shout('Multiple assets found for activated coin ID: $coinId. '
'Expected single asset, found ${assets.length}: $assetIds. ');
_log.shout(
'Multiple assets found for activated coin ID: $coinId. '
'Expected single asset, found ${assets.length}: $assetIds. ',
);
}

// This is expected to throw if there are multiple assets, to stick
// to the strategy of using `.single` elsewhere in the codebase.
walletAssets.add(assets.single);
}

final coinsToSync =
_getWalletCoinsNotInState(walletAssets, coinsBlocWalletCoinsState);
final coinsToSync = _getWalletCoinsNotInState(
walletAssets,
coinsBlocWalletCoinsState,
);
if (coinsToSync.isNotEmpty) {
_log.info(
'Found ${coinsToSync.length} wallet coins not in state, '
Expand All @@ -434,7 +424,9 @@ class CoinsBloc extends Bloc<CoinsEvent, CoinsState> {
}

List<Coin> _getWalletCoinsNotInState(
List<Asset> walletAssets, Map<String, Coin> coinsBlocWalletCoinsState) {
List<Asset> walletAssets,
Map<String, Coin> coinsBlocWalletCoinsState,
) {
final List<Coin> coinsToSyncToState = [];

final enabledAssetsNotInState = walletAssets
Expand Down
18 changes: 8 additions & 10 deletions lib/model/kdf_auth_metadata_extension.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,21 @@ extension KdfAuthMetadataExtension on KomodoDefiSdk {
}

Future<void> addActivatedCoins(Iterable<String> coins) async {
final existingCoins = (await auth.currentUser)
?.metadata
.valueOrNull<List<String>>('activated_coins') ??
final existingCoins =
(await auth.currentUser)?.metadata.valueOrNull<List<String>>(
'activated_coins',
) ??
[];

final mergedCoins = <dynamic>{...existingCoins, ...coins}.toList();
await auth.setOrRemoveActiveUserKeyValue('activated_coins', mergedCoins);
}

Future<void> removeActivatedCoins(List<String> coins) async {
final existingCoins = (await auth.currentUser)
?.metadata
.valueOrNull<List<String>>('activated_coins') ??
final existingCoins =
(await auth.currentUser)?.metadata.valueOrNull<List<String>>(
'activated_coins',
) ??
[];

existingCoins.removeWhere((coin) => coins.contains(coin));
Expand All @@ -53,8 +55,4 @@ extension KdfAuthMetadataExtension on KomodoDefiSdk {
Future<void> confirmSeedBackup({bool hasBackup = true}) async {
await auth.setOrRemoveActiveUserKeyValue('has_backup', hasBackup);
}

Future<void> setWalletType(WalletType type) async {
await auth.setOrRemoveActiveUserKeyValue('type', type.name);
}
}
58 changes: 28 additions & 30 deletions lib/model/wallet.dart
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
import 'package:komodo_defi_sdk/komodo_defi_sdk.dart';
import 'package:komodo_defi_types/komodo_defi_type_utils.dart';
import 'package:komodo_defi_types/komodo_defi_types.dart';
import 'package:komodo_defi_rpc_methods/komodo_defi_rpc_methods.dart'
show PrivateKeyPolicy;
import 'package:uuid/uuid.dart';
import 'package:web_dex/app_config/app_config.dart';
import 'package:web_dex/shared/utils/encryption_tool.dart';

class Wallet {
Wallet({
required this.id,
required this.name,
required this.config,
});
Wallet({required this.id, required this.name, required this.config});

factory Wallet.fromJson(Map<String, dynamic> json) => Wallet(
id: json['id'] as String? ?? '',
name: json['name'] as String? ?? '',
config: WalletConfig.fromJson(
json['config'] as Map<String, dynamic>? ?? {},
),
);
id: json['id'] as String? ?? '',
name: json['name'] as String? ?? '',
config: WalletConfig.fromJson(
json['config'] as Map<String, dynamic>? ?? {},
),
);

/// Creates a wallet from a name and the optional parameters.
/// [name] - The name of the wallet.
Expand Down Expand Up @@ -49,11 +47,7 @@ class Wallet {
required String name,
required WalletConfig config,
}) {
return Wallet(
id: const Uuid().v1(),
name: name,
config: config,
);
return Wallet(id: const Uuid().v1(), name: name, config: config);
}

String id;
Expand All @@ -67,17 +61,13 @@ class Wallet {
await EncryptionTool().decryptData(password, config.seedPhrase) ?? '';

Map<String, dynamic> toJson() => <String, dynamic>{
'id': id,
'name': name,
'config': config.toJson(),
};
'id': id,
'name': name,
'config': config.toJson(),
};

Wallet copy() {
return Wallet(
id: id,
name: name,
config: config.copy(),
);
return Wallet(id: id, name: name, config: config.copy());
}
}

Expand All @@ -93,14 +83,16 @@ class WalletConfig {

factory WalletConfig.fromJson(Map<String, dynamic> json) {
return WalletConfig(
// Legacy wallets may still carry a serialized type. We keep it for
// compatibility, but the app should use SDK auth options as source of truth.
type: WalletType.fromJson(
json['type'] as String? ?? WalletType.iguana.name,
),
seedPhrase: json['seed_phrase'] as String? ?? '',
pubKey: json['pub_key'] as String?,
activatedCoins:
List<String>.from(json['activated_coins'] as List? ?? <String>[])
.toList(),
activatedCoins: List<String>.from(
json['activated_coins'] as List? ?? <String>[],
).toList(),
hasBackup: json['has_backup'] as bool? ?? false,
);
}
Expand All @@ -114,6 +106,7 @@ class WalletConfig {

Map<String, dynamic> toJson() {
return <String, dynamic>{
// Retain type for legacy export. Runtime uses SDK auth options.
'type': type.name,
'seed_phrase': seedPhrase,
'pub_key': pubKey,
Expand Down Expand Up @@ -158,8 +151,13 @@ enum WalletType {

extension KdfUserWalletExtension on KdfUser {
Wallet get wallet {
final walletType =
WalletType.fromJson(metadata['type'] as String? ?? 'iguana');
// Derive wallet type from SDK auth options rather than metadata
final isTrezor =
walletId.authOptions.privKeyPolicy == const PrivateKeyPolicy.trezor();
final isHd = walletId.isHd;
final walletType = isTrezor
? WalletType.trezor
: (isHd ? WalletType.hdwallet : WalletType.iguana);
return Wallet(
id: walletId.name,
name: walletId.name,
Expand Down
Loading
Loading