diff --git a/.docker/komodo-wallet-android.dockerfile b/.docker/komodo-wallet-android.dockerfile index f2a136c92c..d690c4cd91 100644 --- a/.docker/komodo-wallet-android.dockerfile +++ b/.docker/komodo-wallet-android.dockerfile @@ -1,6 +1,6 @@ FROM komodo/android-sdk:35 AS final -ENV FLUTTER_VERSION="3.35.2" +ENV FLUTTER_VERSION="3.35.3" ENV HOME="/home/komodo" ENV USER="komodo" ENV PATH=$PATH:$HOME/flutter/bin @@ -14,4 +14,4 @@ RUN curl -O https://storage.googleapis.com/flutter_infra_release/releases/stable rm flutter_linux_${FLUTTER_VERSION}-stable.tar.xz && \ flutter config --no-analytics && \ yes "y" | flutter doctor --android-licenses && \ - flutter doctor \ No newline at end of file + flutter doctor diff --git a/.github/actions/flutter-deps/action.yml b/.github/actions/flutter-deps/action.yml index e03ac3c2c0..34cb7741de 100644 --- a/.github/actions/flutter-deps/action.yml +++ b/.github/actions/flutter-deps/action.yml @@ -7,7 +7,7 @@ runs: uses: subosito/flutter-action@v2 with: # NB! Keep up-to-date with the flutter version used for development - flutter-version: "3.35.2" + flutter-version: "3.35.3" channel: "stable" - name: Prepare build directory diff --git a/.github/workflows/roll-sdk-packages.yml b/.github/workflows/roll-sdk-packages.yml index 910c98e824..e5ba912c43 100644 --- a/.github/workflows/roll-sdk-packages.yml +++ b/.github/workflows/roll-sdk-packages.yml @@ -46,7 +46,7 @@ jobs: uses: subosito/flutter-action@v2 with: # NB! Keep up-to-date with the flutter version used for development - flutter-version: "3.35.2" + flutter-version: "3.35.3" channel: "stable" - name: Determine configuration @@ -89,7 +89,7 @@ jobs: echo "SDK packages were successfully rolled" else echo "ROLLS_FOUND=false" >> $GITHUB_ENV - + if [ -n "${EXIT_CODE}" ] && [ ${EXIT_CODE} -ne 100 ]; then echo "::warning::SDK package roll script failed with exit code ${EXIT_CODE}" else diff --git a/app_theme/pubspec.yaml b/app_theme/pubspec.yaml index 27e1c6afc1..c4e2bd83bb 100644 --- a/app_theme/pubspec.yaml +++ b/app_theme/pubspec.yaml @@ -7,7 +7,7 @@ resolution: workspace environment: sdk: ">=3.8.1 <4.0.0" - flutter: ">=3.35.2 <4.0.0" + flutter: ">=3.35.3 <4.0.0" dependencies: flutter: diff --git a/docs/FLUTTER_VERSION.md b/docs/FLUTTER_VERSION.md index 09ded4982c..725b92df3e 100644 --- a/docs/FLUTTER_VERSION.md +++ b/docs/FLUTTER_VERSION.md @@ -2,7 +2,7 @@ ## Supported Flutter Version -This project supports Flutter `3.35.2` (latest stable release). We aim to keep the project up-to-date with the most recent stable Flutter versions. +This project supports Flutter `3.35.3` (latest stable release). We aim to keep the project up-to-date with the most recent stable Flutter versions. ## Recommended Approach: Multiple Flutter Versions @@ -15,14 +15,14 @@ See our guide on [Multiple Flutter Versions](MULTIPLE_FLUTTER_VERSIONS.md) for d While it's possible to pin your global Flutter installation to a specific version, **this approach is not recommended** due to: - Lack of isolation between projects -- Known issues with `flutter pub get` when using Flutter 3.35.2 +- Known issues with `flutter pub get` when using Flutter 3.35.3 - Difficulty switching between versions for different projects If you still choose to use this method, you can run: ```bash cd ~/flutter -git checkout 3.35.2 +git checkout 3.35.3 flutter doctor ``` diff --git a/docs/MULTIPLE_FLUTTER_VERSIONS.md b/docs/MULTIPLE_FLUTTER_VERSIONS.md index 22ecb212a3..561083386a 100644 --- a/docs/MULTIPLE_FLUTTER_VERSIONS.md +++ b/docs/MULTIPLE_FLUTTER_VERSIONS.md @@ -57,7 +57,7 @@ sudo pacman -R flutter # for Arch 2. Launch Flutter Sidekick -3. Click on "Versions" in the sidebar and download Flutter version `3.35.2` +3. Click on "Versions" in the sidebar and download Flutter version `3.35.3` 4. Set this version as the global default by clicking the "Set as Global" button @@ -92,11 +92,11 @@ sudo pacman -R flutter # for Arch curl -fsSL https://fvm.app/install.sh | bash ``` -2. Install and use Flutter 3.35.2: +2. Install and use Flutter 3.35.3: ```bash - fvm install 3.35.2 - fvm global 3.35.2 + fvm install 3.35.3 + fvm global 3.35.3 ``` 3. Add FVM's default Flutter version to your PATH by adding the following to your `~/.bashrc`, `~/.zshrc`, or equivalent: @@ -131,11 +131,11 @@ sudo pacman -R flutter # for Arch choco install fvm ``` -3. Install and use Flutter 3.35.2: +3. Install and use Flutter 3.35.3: ```powershell - fvm install 3.35.2 - fvm global 3.35.2 + fvm install 3.35.3 + fvm global 3.35.3 ``` 4. Add FVM's Flutter version to your PATH: @@ -158,7 +158,7 @@ To use a specific Flutter version for a project: 2. Run: ```bash - fvm use 3.35.2 + fvm use 3.35.3 ``` This will create a `.fvmrc` file in your project, which specifies the Flutter version to use for this project. diff --git a/lib/bloc/auth_bloc/auth_bloc.dart b/lib/bloc/auth_bloc/auth_bloc.dart index 418be2f0de..cdcb58f346 100644 --- a/lib/bloc/auth_bloc/auth_bloc.dart +++ b/lib/bloc/auth_bloc/auth_bloc.dart @@ -42,6 +42,7 @@ class AuthBloc extends Bloc with TrezorAuthMixin { final WalletsRepository _walletsRepository; final SettingsRepository _settingsRepository; StreamSubscription? _authChangesSubscription; + @override final _log = Logger('AuthBloc'); @override @@ -91,11 +92,10 @@ class AuthBloc extends Bloc with TrezorAuthMixin { ); } - _log.info('login from a wallet'); emit(AuthBlocState.loading()); + _log.info('Logging in to an existing wallet.'); final weakPasswordsAllowed = await _areWeakPasswordsAllowed(); - await _kdfSdk.auth.signIn( walletName: event.wallet.name, password: event.password, @@ -111,7 +111,7 @@ class AuthBloc extends Bloc with TrezorAuthMixin { return emit(AuthBlocState.error(AuthException.notSignedIn())); } - _log.info('logged in from a wallet'); + _log.info('Successfully logged in to wallet'); emit(AuthBlocState.loggedIn(currentUser)); _listenToAuthStateChanges(); } catch (e, s) { @@ -159,13 +159,17 @@ class AuthBloc extends Bloc with TrezorAuthMixin { try { emit(AuthBlocState.loading()); if (await _didSignInExistingWallet(event.wallet, event.password)) { + add( + AuthSignInRequested(wallet: event.wallet, password: event.password), + ); + _log.warning( + 'Wallet ${event.wallet.name} already exists, attempting sign-in', + ); return; } - _log.info('register from a wallet'); - + _log.info('Registering a new wallet'); final weakPasswordsAllowed = await _areWeakPasswordsAllowed(); - await _kdfSdk.auth.register( password: event.password, walletName: event.wallet.name, @@ -177,7 +181,9 @@ class AuthBloc extends Bloc with TrezorAuthMixin { ), ); - _log.info('registered from a wallet'); + _log.info( + 'Registered a new wallet, setting up metadata and logging in...', + ); await _kdfSdk.setWalletType(event.wallet.config.type); await _kdfSdk.confirmSeedBackup(hasBackup: false); await _kdfSdk.addActivatedCoins(enabledByDefaultCoins); @@ -205,15 +211,19 @@ class AuthBloc extends Bloc with TrezorAuthMixin { Emitter emit, ) async { try { - emit(AuthBlocState.loading()); if (await _didSignInExistingWallet(event.wallet, event.password)) { + add( + AuthSignInRequested(wallet: event.wallet, password: event.password), + ); + _log.warning( + 'Wallet ${event.wallet.name} already exists, attempting sign-in', + ); return; } - _log.info('restore from a wallet'); - + emit(AuthBlocState.loading()); + _log.info('Restoring wallet from a seed'); final weakPasswordsAllowed = await _areWeakPasswordsAllowed(); - await _kdfSdk.auth.register( password: event.password, walletName: event.wallet.name, @@ -226,27 +236,36 @@ class AuthBloc extends Bloc with TrezorAuthMixin { ), ); - _log.info('restored from a wallet'); + _log.info( + 'Successfully restored wallet from a seed. ' + 'Setting up wallet metadata and logging in...', + ); await _kdfSdk.setWalletType(event.wallet.config.type); await _kdfSdk.confirmSeedBackup(hasBackup: event.wallet.config.hasBackup); await _kdfSdk.addActivatedCoins(enabledByDefaultCoins); - - final currentUser = await _kdfSdk.auth.currentUser; - if (currentUser == null) { - throw Exception('Registration failed: user is not signed in'); + if (event.wallet.config.activatedCoins.isNotEmpty) { + await _kdfSdk.addActivatedCoins(event.wallet.config.activatedCoins); } - emit(AuthBlocState.loggedIn(currentUser)); // Delete legacy wallet on successful restoration & login to avoid // duplicates in the wallet list if (event.wallet.isLegacyWallet) { - await _kdfSdk.addActivatedCoins(event.wallet.config.activatedCoins); + _log.info( + 'Migration successful. ' + 'Deleting legacy wallet ${event.wallet.name}', + ); await _walletsRepository.deleteWallet( event.wallet, password: event.password, ); } + final currentUser = await _kdfSdk.auth.currentUser; + if (currentUser == null) { + throw Exception('Restoration from seed failed: user is not signed in'); + } + + emit(AuthBlocState.loggedIn(currentUser)); _listenToAuthStateChanges(); } catch (e, s) { final errorMsg = 'Failed to restore existing wallet ${event.wallet.name}'; @@ -266,8 +285,6 @@ class AuthBloc extends Bloc with TrezorAuthMixin { (KdfUser user) => user.walletId.name == wallet.name, ); if (walletExists) { - add(AuthSignInRequested(wallet: wallet, password: password)); - _log.warning('Wallet ${wallet.name} already exist, attempting sign-in'); return true; } diff --git a/lib/blocs/wallets_repository.dart b/lib/blocs/wallets_repository.dart index eb449ddc1b..693f2a4860 100644 --- a/lib/blocs/wallets_repository.dart +++ b/lib/blocs/wallets_repository.dart @@ -20,8 +20,8 @@ class WalletsRepository { this._legacyWalletStorage, { EncryptionTool? encryptionTool, FileLoader? fileLoader, - }) : _encryptionTool = encryptionTool ?? EncryptionTool(), - _fileLoader = fileLoader ?? FileLoader.fromPlatform(); + }) : _encryptionTool = encryptionTool ?? EncryptionTool(), + _fileLoader = fileLoader ?? FileLoader.fromPlatform(); final KomodoDefiSdk _kdfSdk; final Mm2Api _mm2Api; @@ -34,9 +34,10 @@ class WalletsRepository { Future> getWallets() async { final legacyWallets = await _getLegacyWallets(); + final sdkWallets = await _kdfSdk.wallets; // TODO: move wallet filtering logic to the SDK - _cachedWallets = (await _kdfSdk.wallets) + _cachedWallets = sdkWallets .where( (wallet) => wallet.config.type != WalletType.trezor && @@ -47,21 +48,26 @@ class WalletsRepository { } Future> _getLegacyWallets() async { - final newVariable = - await _legacyWalletStorage.read(allWalletsStorageKey) as List?; - final List> json = - newVariable?.cast>() ?? >[]; - - return json - .map((Map w) => - Wallet.fromJson(w)..config.isLegacyWallet = true) - .toList(); + final rawLegacyWallets = + (await _legacyWalletStorage.read(allWalletsStorageKey) as List?) + ?.cast>() ?? + []; + + return rawLegacyWallets.map((Map w) { + final wallet = Wallet.fromJson(w); + return wallet.copyWith( + config: wallet.config.copyWith( + // Wallet type for legacy wallets is iguana, to avoid confusion with + // missing/empty balances. Sign into iguana for legacy wallets by + // default, but allow for them to be signed into hdwallet if desired. + type: WalletType.iguana, + isLegacyWallet: true, + ), + ); + }).toList(); } - Future deleteWallet( - Wallet wallet, { - required String password, - }) async { + Future deleteWallet(Wallet wallet, {required String password}) async { log( 'Deleting a wallet ${wallet.id}', path: 'wallet_bloc => deleteWallet', @@ -82,9 +88,11 @@ class WalletsRepository { _cachedWallets?.removeWhere((w) => w.name == wallet.name); return; } catch (e) { - log('Failed to delete wallet: $e', - path: 'wallet_bloc => deleteWallet', isError: true) - .ignore(); + log( + 'Failed to delete wallet: $e', + path: 'wallet_bloc => deleteWallet', + isError: true, + ).ignore(); rethrow; } } @@ -99,14 +107,14 @@ class WalletsRepository { getWallets().ignore(); return null; } - + final trimmedName = name.trim(); - + // Check if the trimmed name is empty (prevents space-only names) if (trimmedName.isEmpty) { return LocaleKeys.walletCreationNameLengthError.tr(); } - + // Check if trimmed name exceeds length limit if (trimmedName.length > 40) { return LocaleKeys.walletCreationNameLengthError.tr(); @@ -122,8 +130,9 @@ class WalletsRepository { } Future resetSpecificWallet(Wallet wallet) async { - final coinsToDeactivate = wallet.config.activatedCoins - .where((coin) => !enabledByDefaultCoins.contains(coin)); + final coinsToDeactivate = wallet.config.activatedCoins.where( + (coin) => !enabledByDefaultCoins.contains(coin), + ); for (final coin in coinsToDeactivate) { await _mm2Api.disableCoin(coin); } @@ -140,8 +149,10 @@ class WalletsRepository { ); } final String data = jsonEncode(wallet.config); - final String encryptedData = - await _encryptionTool.encryptData(password, data); + final String encryptedData = await _encryptionTool.encryptData( + password, + data, + ); final String sanitizedFileName = _sanitizeFileName(wallet.name); await _fileLoader.save( fileName: sanitizedFileName, diff --git a/lib/model/wallet.dart b/lib/model/wallet.dart index 2a18f8637f..518d4c4317 100644 --- a/lib/model/wallet.dart +++ b/lib/model/wallet.dart @@ -79,6 +79,18 @@ class Wallet { config: config.copy(), ); } + + Wallet copyWith({ + String? id, + String? name, + WalletConfig? config, + }) { + return Wallet( + id: id ?? this.id, + name: name ?? this.name, + config: config ?? this.config.copy(), + ); + } } class WalletConfig { @@ -131,6 +143,24 @@ class WalletConfig { pubKey: pubKey, ); } + + WalletConfig copyWith({ + String? seedPhrase, + String? pubKey, + List? activatedCoins, + bool? hasBackup, + WalletType? type, + bool? isLegacyWallet, + }) { + return WalletConfig( + seedPhrase: seedPhrase ?? this.seedPhrase, + pubKey: pubKey ?? this.pubKey, + activatedCoins: activatedCoins ?? [...this.activatedCoins], + hasBackup: hasBackup ?? this.hasBackup, + type: type ?? this.type, + isLegacyWallet: isLegacyWallet ?? this.isLegacyWallet, + ); + } } enum WalletType { diff --git a/lib/views/wallet/wallet_page/common/grouped_asset_ticker_item.dart b/lib/views/wallet/wallet_page/common/grouped_asset_ticker_item.dart index f2383fc91b..15ff7ef7de 100644 --- a/lib/views/wallet/wallet_page/common/grouped_asset_ticker_item.dart +++ b/lib/views/wallet/wallet_page/common/grouped_asset_ticker_item.dart @@ -122,8 +122,11 @@ class _GroupedAssetTickerItemState extends State { flex: 2, child: BlocBuilder( builder: (context, state) { - final formattedPrice = price?.price != null - ? priceFormatter.format(price!.price) + // Double conversion required to fix the + // `noSuchMethod` error in the `format` method. + final priceValue = price?.price?.toDouble(); + final formattedPrice = priceValue != null + ? priceFormatter.format(priceValue) : ''; return Text( formattedPrice, diff --git a/packages/komodo_ui_kit/pubspec.yaml b/packages/komodo_ui_kit/pubspec.yaml index 2dc20bf43d..eca3b99ebf 100644 --- a/packages/komodo_ui_kit/pubspec.yaml +++ b/packages/komodo_ui_kit/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=3.8.1 <4.0.0" - flutter: ">=3.35.2 <4.0.0" + flutter: ">=3.35.3 <4.0.0" resolution: workspace diff --git a/pubspec.lock b/pubspec.lock index 1c10fa4bc9..ca2aeb0638 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1576,4 +1576,4 @@ packages: version: "3.1.3" sdks: dart: ">=3.8.1 <4.0.0" - flutter: ">=3.35.2" + flutter: ">=3.35.3" diff --git a/pubspec.yaml b/pubspec.yaml index c2d407a111..2f25cf6556 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -19,7 +19,7 @@ version: 0.9.2+0 environment: sdk: ">=3.8.1 <4.0.0" - flutter: ">=3.35.2 <4.0.0" + flutter: ">=3.35.3 <4.0.0" workspace: - packages/komodo_ui_kit