diff --git a/assets/translations/en.json b/assets/translations/en.json index 8bb2aeb441..48250e5351 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -333,7 +333,7 @@ "trezorEnterPinHint": "The PIN layout is displayed on your Trezor", "trezorInProgressTitle": "Looking for your Trezor...", "trezorInProgressHint": "Please do not disable your Trezor", - "trezorErrorBusy": "Device is busy.\n' Make sure that Trezor Suite is not running in background.", + "trezorErrorBusy": "Device is busy.\nMake sure that Trezor Suite is not running in background.", "trezorErrorInvalidPin": "Invalid PIN code", "trezorSelectTitle": "Connect a hardware wallet", "trezorSelectSubTitle": "Select a hardware wallet you'd like to use with Komodo Wallet", diff --git a/lib/app_config/app_config.dart b/lib/app_config/app_config.dart index 85417c6d61..b940a7c198 100644 --- a/lib/app_config/app_config.dart +++ b/lib/app_config/app_config.dart @@ -92,15 +92,6 @@ const Set excludedAssetList = { 'NFT_MATIC', }; -const List excludedAssetListTrezor = [ - // https://github.com/KomodoPlatform/atomicDEX-API/issues/1510 - 'BCH', - // https://github.com/KomodoPlatform/coins/pull/619/files - // Can't use modified config directly, since it includes features, - // not implemented on webdex side yet (e.g. 0.4.2 doesn't have segwit) - 'VAL', -]; - /// Some coins returned by the Banxa API are returning errors when attempting /// to create an order. This is a temporary workaround to filter out those coins /// until the issue is resolved. @@ -150,12 +141,6 @@ List get enabledByDefaultCoins => [ if (kDebugMode) 'MARTY', ]; -List get enabledByDefaultTrezorCoins => [ - 'BTC-segwit', - 'KMD', - 'LTC-segwit', - ]; - List get coinsWithFaucet => ['RICK', 'MORTY', 'DOC', 'MARTY']; const String logsDbName = 'logs'; diff --git a/lib/bloc/app_bloc_root.dart b/lib/bloc/app_bloc_root.dart index c7da1eb5b4..d542180fc8 100644 --- a/lib/bloc/app_bloc_root.dart +++ b/lib/bloc/app_bloc_root.dart @@ -49,14 +49,10 @@ import 'package:web_dex/bloc/taker_form/taker_bloc.dart'; import 'package:web_dex/bloc/trading_status/trading_status_repository.dart'; import 'package:web_dex/bloc/transaction_history/transaction_history_bloc.dart'; import 'package:web_dex/bloc/transaction_history/transaction_history_repo.dart'; -import 'package:web_dex/bloc/trezor_bloc/trezor_repo.dart'; -import 'package:web_dex/bloc/trezor_connection_bloc/trezor_connection_bloc.dart'; -import 'package:web_dex/bloc/trezor_init_bloc/trezor_init_bloc.dart'; import 'package:web_dex/blocs/kmd_rewards_bloc.dart'; import 'package:web_dex/blocs/maker_form_bloc.dart'; import 'package:web_dex/blocs/orderbook_bloc.dart'; import 'package:web_dex/blocs/trading_entities_bloc.dart'; -import 'package:web_dex/blocs/trezor_coins_bloc.dart'; import 'package:web_dex/blocs/wallets_repository.dart'; import 'package:web_dex/main.dart'; import 'package:web_dex/mm2/mm2_api/mm2_api.dart'; @@ -117,8 +113,6 @@ class AppBlocRoot extends StatelessWidget { myOrdersService, ); final dexRepository = DexRepository(mm2Api); - final trezorRepo = RepositoryProvider.of(context); - final trezorBloc = RepositoryProvider.of(context); final transactionsRepo = performanceMode != null ? MockTransactionHistoryRepo( @@ -185,7 +179,6 @@ class AppBlocRoot extends StatelessWidget { create: (context) => CoinsBloc( komodoDefiSdk, coinsRepository, - trezorBloc, mm2Api, )..add(CoinsStarted()), ), @@ -256,13 +249,6 @@ class AppBlocRoot extends StatelessWidget { analyticsBloc: BlocProvider.of(context), ), ), - BlocProvider( - create: (_) => TrezorConnectionBloc( - trezorRepo: trezorRepo, - kdfSdk: komodoDefiSdk, - ), - lazy: false, - ), BlocProvider( lazy: false, create: (context) => NftMainBloc( @@ -298,13 +284,6 @@ class AppBlocRoot extends StatelessWidget { create: (_) => SystemHealthBloc(SystemClockRepository(), mm2Api) ..add(SystemHealthPeriodicCheckStarted()), ), - BlocProvider( - create: (context) => TrezorInitBloc( - kdfSdk: komodoDefiSdk, - trezorRepo: trezorRepo, - coinsRepository: coinsRepository, - ), - ), BlocProvider( create: (context) => CoinsManagerBloc( coinsRepo: coinsRepository, diff --git a/lib/bloc/auth_bloc/auth_bloc.dart b/lib/bloc/auth_bloc/auth_bloc.dart index 3fe2189e6f..a048c770d0 100644 --- a/lib/bloc/auth_bloc/auth_bloc.dart +++ b/lib/bloc/auth_bloc/auth_bloc.dart @@ -5,6 +5,8 @@ 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'; import 'package:logging/logging.dart'; +import 'package:komodo_defi_rpc_methods/komodo_defi_rpc_methods.dart' + show PrivateKeyPolicy; import 'package:web_dex/app_config/app_config.dart'; import 'package:web_dex/bloc/settings/settings_repository.dart'; import 'package:web_dex/blocs/wallets_repository.dart'; @@ -14,10 +16,11 @@ import 'package:web_dex/model/wallet.dart'; part 'auth_bloc_event.dart'; part 'auth_bloc_state.dart'; +part 'trezor_auth_mixin.dart'; /// AuthBloc is responsible for managing the authentication state of the /// application. It handles events such as login and logout changes. -class AuthBloc extends Bloc { +class AuthBloc extends Bloc 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) @@ -31,6 +34,7 @@ class AuthBloc extends Bloc { on(_onSeedBackupConfirmed); on(_onWalletDownloadRequested); on(_onLifecycleCheckRequested); + setupTrezorEventHandlers(); } final KomodoDefiSdk _kdfSdk; @@ -39,6 +43,9 @@ class AuthBloc extends Bloc { StreamSubscription? _authChangesSubscription; final _log = Logger('AuthBloc'); + @override + KomodoDefiSdk get _sdk => _kdfSdk; + @override Future close() async { await _authChangesSubscription?.cancel(); diff --git a/lib/bloc/auth_bloc/auth_bloc_event.dart b/lib/bloc/auth_bloc/auth_bloc_event.dart index b3b38f9e12..077d24cfd6 100644 --- a/lib/bloc/auth_bloc/auth_bloc_event.dart +++ b/lib/bloc/auth_bloc/auth_bloc_event.dart @@ -54,6 +54,26 @@ class AuthWalletDownloadRequested extends AuthBlocEvent { final String password; } +class AuthTrezorInitAndAuthStarted extends AuthBlocEvent { + const AuthTrezorInitAndAuthStarted(); +} + +class AuthTrezorPinProvided extends AuthBlocEvent { + const AuthTrezorPinProvided(this.pin); + + final String pin; +} + +class AuthTrezorPassphraseProvided extends AuthBlocEvent { + const AuthTrezorPassphraseProvided(this.passphrase); + + final String passphrase; +} + +class AuthTrezorCancelled extends AuthBlocEvent { + const AuthTrezorCancelled(); +} + /// Event emitted on app lifecycle changes to check if a user is already signed /// in and restore the auth state. class AuthLifecycleCheckRequested extends AuthBlocEvent { diff --git a/lib/bloc/auth_bloc/auth_bloc_state.dart b/lib/bloc/auth_bloc/auth_bloc_state.dart index 0ff220605e..a1efa8fa19 100644 --- a/lib/bloc/auth_bloc/auth_bloc_state.dart +++ b/lib/bloc/auth_bloc/auth_bloc_state.dart @@ -1,12 +1,10 @@ part of 'auth_bloc.dart'; -enum AuthStatus { initial, loading, success, failure } - class AuthBlocState extends Equatable { const AuthBlocState({ required this.mode, this.currentUser, - this.status = AuthStatus.initial, + this.authenticationState, this.authError, }); @@ -14,28 +12,77 @@ class AuthBlocState extends Equatable { const AuthBlocState(mode: AuthorizeMode.noLogin); factory AuthBlocState.loading() => const AuthBlocState( mode: AuthorizeMode.noLogin, - status: AuthStatus.loading, + authenticationState: + AuthenticationState(status: AuthenticationStatus.initializing), ); factory AuthBlocState.error(AuthException authError) => AuthBlocState( mode: AuthorizeMode.noLogin, - status: AuthStatus.failure, - authError: authError, + authenticationState: AuthenticationState.error(authError.toString()), ); factory AuthBlocState.loggedIn(KdfUser user) => AuthBlocState( mode: AuthorizeMode.logIn, - status: AuthStatus.success, + authenticationState: AuthenticationState.completed(user), currentUser: user, ); + factory AuthBlocState.trezorInitializing({String? message, int? taskId}) => + AuthBlocState( + mode: AuthorizeMode.noLogin, + authenticationState: AuthenticationState( + status: AuthenticationStatus.initializing, + taskId: taskId, + message: message, + ), + ); + factory AuthBlocState.trezorAwaitingConfirmation( + {String? message, int? taskId}) => + AuthBlocState( + mode: AuthorizeMode.noLogin, + authenticationState: AuthenticationState( + status: AuthenticationStatus.waitingForDeviceConfirmation, + taskId: taskId, + message: message, + ), + ); + factory AuthBlocState.trezorPinRequired( + {String? message, required int taskId}) => + AuthBlocState( + mode: AuthorizeMode.noLogin, + authenticationState: AuthenticationState( + status: AuthenticationStatus.pinRequired, + taskId: taskId, + message: message, + ), + ); + factory AuthBlocState.trezorPassphraseRequired( + {String? message, required int taskId}) => + AuthBlocState( + mode: AuthorizeMode.noLogin, + authenticationState: AuthenticationState( + status: AuthenticationStatus.passphraseRequired, + taskId: taskId, + message: message, + ), + ); + factory AuthBlocState.trezorReady() => const AuthBlocState( + mode: AuthorizeMode.noLogin, + authenticationState: + AuthenticationState(status: AuthenticationStatus.cancelled), + ); final KdfUser? currentUser; final AuthorizeMode mode; - final AuthStatus status; + final AuthenticationState? authenticationState; final AuthException? authError; + AuthenticationStatus? get status => authenticationState?.status; + bool get isSignedIn => currentUser != null; - bool get isLoading => status == AuthStatus.loading; - bool get isError => status == AuthStatus.failure; + bool get isLoading => + status == AuthenticationStatus.authenticating || + status == AuthenticationStatus.initializing; + bool get isError => status == AuthenticationStatus.error; @override - List get props => [mode, currentUser, status, authError]; + List get props => + [mode, currentUser, status, authError, authenticationState]; } diff --git a/lib/bloc/auth_bloc/trezor_auth_mixin.dart b/lib/bloc/auth_bloc/trezor_auth_mixin.dart new file mode 100644 index 0000000000..e419c93c54 --- /dev/null +++ b/lib/bloc/auth_bloc/trezor_auth_mixin.dart @@ -0,0 +1,181 @@ +part of 'auth_bloc.dart'; + +/// Mixin that exposes Trezor authentication helpers for [AuthBloc]. +mixin TrezorAuthMixin on Bloc { + KomodoDefiSdk get _sdk; + final _log = Logger('TrezorAuthMixin'); + + /// Registers handlers for Trezor specific events. + void setupTrezorEventHandlers() { + on(_onTrezorInitAndAuth); + on(_onTrezorProvidePin); + on(_onTrezorProvidePassphrase); + on(_onTrezorCancel); + } + + Future _onTrezorInitAndAuth( + AuthTrezorInitAndAuthStarted event, + Emitter emit, + ) async { + try { + final authOptions = AuthOptions( + derivationMethod: DerivationMethod.hdWallet, + privKeyPolicy: const PrivateKeyPolicy.trezor(), + ); + + final Stream authStream = _sdk.auth.signInStream( + walletName: '', // handled internally by sdk + password: '', // handled internally by sdk + options: authOptions, + ); + + await for (final authState in authStream) { + final mappedState = await _handleAuthenticationState(authState); + emit(mappedState); + if (authState.status == AuthenticationStatus.completed || + authState.status == AuthenticationStatus.error || + authState.status == AuthenticationStatus.cancelled) { + break; + } + } + } catch (e) { + _log.shout('Trezor authentication failed', e); + emit( + AuthBlocState.error( + AuthException( + e.toString(), + type: AuthExceptionType.generalAuthError, + ), + ), + ); + } + } + + Future _handleAuthenticationState( + AuthenticationState authState) async { + switch (authState.status) { + case AuthenticationStatus.initializing: + return AuthBlocState.trezorInitializing( + message: authState.message ?? 'Initializing Trezor device...', + taskId: authState.taskId, + ); + case AuthenticationStatus.waitingForDevice: + return AuthBlocState.trezorInitializing( + message: + authState.message ?? 'Waiting for Trezor device connection...', + taskId: authState.taskId, + ); + case AuthenticationStatus.waitingForDeviceConfirmation: + return AuthBlocState.trezorAwaitingConfirmation( + message: authState.message ?? + 'Please follow instructions on your Trezor device', + taskId: authState.taskId, + ); + case AuthenticationStatus.pinRequired: + return AuthBlocState.trezorPinRequired( + message: authState.message ?? 'Please enter your Trezor PIN', + taskId: authState.taskId!, + ); + case AuthenticationStatus.passphraseRequired: + return AuthBlocState.trezorPassphraseRequired( + message: authState.message ?? 'Please enter your Trezor passphrase', + taskId: authState.taskId!, + ); + case AuthenticationStatus.authenticating: + return AuthBlocState.loading(); + case AuthenticationStatus.completed: + return await _setupTrezorWallet(authState); + case AuthenticationStatus.error: + return AuthBlocState.error(AuthException( + authState.error ?? 'Trezor authentication failed', + type: AuthExceptionType.generalAuthError, + )); + case AuthenticationStatus.cancelled: + return AuthBlocState.error(AuthException( + 'Trezor authentication was cancelled', + type: AuthExceptionType.generalAuthError, + )); + } + } + + /// Sets up the Trezor wallet after successful authentication. + /// This includes setting the wallet type, confirming seed backup, + /// and adding the default activated coins. + Future _setupTrezorWallet( + AuthenticationState authState, + ) async { + // This should not happen, but if it does then trezor initialization failed + // and we should not proceed. + if (authState.user == null) { + return AuthBlocState.error(AuthException( + 'Trezor initialization failed', + type: AuthExceptionType.generalAuthError, + )); + } + + 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 + // the user is setting up their Trezor wallet. + await _sdk.addActivatedCoins(enabledByDefaultCoins); + } + + // Refresh the current user to pull in the updated wallet metadata + // configured above. + final updatedUser = await _sdk.auth.currentUser; + return AuthBlocState.loggedIn(updatedUser!); + } + + Future _onTrezorProvidePin( + AuthTrezorPinProvided event, + Emitter emit, + ) async { + try { + final taskId = state.authenticationState?.taskId; + if (taskId == null) { + emit(AuthBlocState.error(AuthException('No task ID found', + type: AuthExceptionType.generalAuthError))); + return; + } + + await _sdk.auth.setHardwareDevicePin(taskId, event.pin); + } catch (e) { + _log.shout('Failed to provide PIN', e); + emit(AuthBlocState.error(AuthException('Failed to provide PIN', + type: AuthExceptionType.generalAuthError))); + } + } + + Future _onTrezorProvidePassphrase( + AuthTrezorPassphraseProvided event, + Emitter emit, + ) async { + try { + final taskId = state.authenticationState?.taskId; + if (taskId == null) { + emit(AuthBlocState.error(AuthException('No task ID found', + type: AuthExceptionType.generalAuthError))); + return; + } + + await _sdk.auth.setHardwareDevicePassphrase( + taskId, + event.passphrase, + ); + } catch (e) { + _log.shout('Failed to provide passphrase', e); + emit(AuthBlocState.error(AuthException( + 'Failed to provide passphrase', + type: AuthExceptionType.generalAuthError, + ))); + } + } + + Future _onTrezorCancel( + AuthTrezorCancelled event, + Emitter emit, + ) async { + emit(AuthBlocState.initial()); + } +} diff --git a/lib/bloc/cex_market_data/price_chart/price_chart_bloc.dart b/lib/bloc/cex_market_data/price_chart/price_chart_bloc.dart index ce46224f73..1412335485 100644 --- a/lib/bloc/cex_market_data/price_chart/price_chart_bloc.dart +++ b/lib/bloc/cex_market_data/price_chart/price_chart_bloc.dart @@ -1,6 +1,8 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:komodo_cex_market_data/komodo_cex_market_data.dart'; import 'package:komodo_defi_sdk/komodo_defi_sdk.dart'; +import 'package:komodo_defi_types/komodo_defi_types.dart'; +import 'package:web_dex/bloc/coins_bloc/asset_coin_extension.dart'; import 'package:web_dex/shared/utils/utils.dart'; import 'models/price_chart_data.dart'; @@ -27,55 +29,17 @@ class PriceChartBloc extends Bloc { ) async { emit(state.copyWith(status: PriceChartStatus.loading)); try { - final coinPrices = await _komodoPriceRepository.getKomodoPrices(); - - Map fetchedCexCoins = state.availableCoins; + Map fetchedCexCoins = state.availableCoins; if (state.availableCoins.isEmpty) { - final coins = (await cexPriceRepository.getCoinList()) - .where((coin) => coin.currencies.contains('USDT')) - // `cexPriceRepository.getCoinList()` returns coins from a CEX - // (e.g. Binance), some of which are not in our known/available - // assets/coins list. This filter ensures that we only attempt to - // fetch and display data for supported coins - .where((coin) => sdk.assets.assetsFromTicker(coin.id).isNotEmpty) - .map((coin) async { - double? dayChangePercent = coinPrices[coin.symbol]?.change24h; - - if (dayChangePercent == null) { - try { - final coinOhlc = await cexPriceRepository.getCoinOhlc( - CexCoinPair.usdtPrice(coin.symbol), - GraphInterval.oneMinute, - startAt: DateTime.now().subtract(const Duration(days: 1)), - endAt: DateTime.now(), - ); - - dayChangePercent = _calculatePercentageChange( - coinOhlc.ohlc.firstOrNull, - coinOhlc.ohlc.lastOrNull, - ); - } catch (e) { - log("Error fetching OHLC data for ${coin.symbol}: $e"); - } - } - return CoinPriceInfo( - ticker: coin.symbol, - id: coin.id, - name: coin.name, - selectedPeriodIncreasePercentage: dayChangePercent ?? 0.0, - ); - }).toList(); - - fetchedCexCoins = { - for (var coin in await Future.wait(coins)) coin.ticker: coin, - }; + fetchedCexCoins = await _fetchCoinsFromCex(); } - final List> futures = - event.symbols.map((symbol) async { + final List> futures = event.symbols + .map((symbol) => sdk.getSdkAsset(symbol).id) + .map((symbol) async { try { final CoinOhlc ohlcData = await cexPriceRepository.getCoinOhlc( - CexCoinPair.usdtPrice(symbol), + CexCoinPair.usdtPrice(symbol.symbol.assetConfigId), _dividePeriodToInterval(event.period), startAt: DateTime.now().subtract(event.period), endAt: DateTime.now(), @@ -88,7 +52,7 @@ class PriceChartBloc extends Bloc { return PriceChartDataSeries( info: CoinPriceInfo( - ticker: symbol, + ticker: symbol.symbol.assetConfigId, id: fetchedCexCoins[symbol]!.id, name: fetchedCexCoins[symbol]!.name, selectedPeriodIncreasePercentage: rangeChangePercent ?? 0.0, @@ -129,6 +93,51 @@ class PriceChartBloc extends Bloc { } } + Future> _fetchCoinsFromCex() async { + final coinPrices = await _komodoPriceRepository.getKomodoPrices(); + final coins = (await cexPriceRepository.getCoinList()) + .where((coin) => coin.currencies.contains('USDT')) + // `cexPriceRepository.getCoinList()` returns coins from a CEX + // (e.g. Binance), some of which are not in our known/available + // assets/coins list. This filter ensures that we only attempt to + // fetch and display data for supported coins + .where((coin) => sdk.assets.assetsFromTicker(coin.id).isNotEmpty) + .map((coin) async { + double? dayChangePercent = coinPrices[coin.symbol]?.change24h; + + if (dayChangePercent == null) { + try { + final coinOhlc = await cexPriceRepository.getCoinOhlc( + CexCoinPair.usdtPrice(coin.symbol), + GraphInterval.oneMinute, + startAt: DateTime.now().subtract(const Duration(days: 1)), + endAt: DateTime.now(), + ); + + dayChangePercent = _calculatePercentageChange( + coinOhlc.ohlc.firstOrNull, + coinOhlc.ohlc.lastOrNull, + ); + } catch (e) { + log("Error fetching OHLC data for ${coin.symbol}: $e"); + } + } + return CoinPriceInfo( + ticker: coin.symbol, + id: coin.id, + name: coin.name, + selectedPeriodIncreasePercentage: dayChangePercent ?? 0.0, + ); + }).toList(); + + final fetchedCexCoins = { + for (var coin in await Future.wait(coins)) + sdk.getSdkAsset(coin.ticker).id: coin, + }; + + return fetchedCexCoins; + } + double? _calculatePercentageChange(Ohlc? first, Ohlc? last) { if (first == null || last == null) { return null; diff --git a/lib/bloc/cex_market_data/price_chart/price_chart_state.dart b/lib/bloc/cex_market_data/price_chart/price_chart_state.dart index a20add72ec..57bc605b26 100644 --- a/lib/bloc/cex_market_data/price_chart/price_chart_state.dart +++ b/lib/bloc/cex_market_data/price_chart/price_chart_state.dart @@ -1,4 +1,5 @@ import 'package:equatable/equatable.dart'; +import 'package:komodo_defi_types/komodo_defi_types.dart'; import 'package:web_dex/bloc/cex_market_data/price_chart/models/price_chart_data.dart'; enum PriceChartStatus { initial, loading, success, failure } @@ -8,7 +9,7 @@ final class PriceChartState extends Equatable { final List data; final String? error; - final Map availableCoins; + final Map availableCoins; //! final Duration selectedPeriod; @@ -28,7 +29,7 @@ final class PriceChartState extends Equatable { List? data, String? error, Duration? selectedPeriod, - Map? availableCoins, + Map? availableCoins, }) { return PriceChartState( status: status ?? this.status, diff --git a/lib/bloc/coin_addresses/bloc/coin_addresses_bloc.dart b/lib/bloc/coin_addresses/bloc/coin_addresses_bloc.dart index 89470afa60..7501636e27 100644 --- a/lib/bloc/coin_addresses/bloc/coin_addresses_bloc.dart +++ b/lib/bloc/coin_addresses/bloc/coin_addresses_bloc.dart @@ -1,11 +1,11 @@ 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 NewAddressStatus; +import 'package:web_dex/analytics/events.dart'; import 'package:web_dex/bloc/analytics/analytics_bloc.dart'; -import 'package:web_dex/bloc/analytics/analytics_event.dart'; import 'package:web_dex/bloc/coin_addresses/bloc/coin_addresses_event.dart'; import 'package:web_dex/bloc/coin_addresses/bloc/coin_addresses_state.dart'; import 'package:web_dex/bloc/coins_bloc/asset_coin_extension.dart'; -import 'package:web_dex/analytics/events.dart'; class CoinAddressesBloc extends Bloc { final KomodoDefiSdk sdk; @@ -25,54 +25,64 @@ class CoinAddressesBloc extends Bloc { SubmitCreateAddressEvent event, Emitter emit, ) async { - emit(state.copyWith(createAddressStatus: () => FormStatus.submitting)); + emit( + state.copyWith( + createAddressStatus: () => FormStatus.submitting, + newAddressState: () => null, + ), + ); - const maxAttempts = 3; - int attempts = 0; - Exception? lastException; + final stream = sdk.pubkeys.createNewPubkeyStream(getSdkAsset(sdk, assetId)); - while (attempts < maxAttempts) { - attempts++; - try { - final newKey = - await sdk.pubkeys.createNewPubkey(getSdkAsset(sdk, assetId)); + await for (final newAddressState in stream) { + emit(state.copyWith(newAddressState: () => newAddressState)); - final derivation = (newKey as dynamic).derivationPath as String?; - if (derivation != null) { - final parsed = parseDerivationPath(derivation); - analyticsBloc.logEvent( - HdAddressGeneratedEventData( - accountIndex: parsed.accountIndex, - addressIndex: parsed.addressIndex, - assetSymbol: assetId, - ), - ); - } + 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, + ), + ); + } - add(const LoadAddressesEvent()); + add(const LoadAddressesEvent()); - emit( - state.copyWith( - createAddressStatus: () => FormStatus.success, - ), - ); - return; - } catch (e) { - lastException = e is Exception ? e : Exception(e.toString()); - if (attempts >= maxAttempts) { + 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; - } - await Future.delayed(Duration(milliseconds: 500 * attempts)); } } - - emit( - state.copyWith( - createAddressStatus: () => FormStatus.failure, - errorMessage: () => - 'Failed after $attempts attempts: ${lastException.toString()}', - ), - ); } Future _onLoadAddresses( diff --git a/lib/bloc/coin_addresses/bloc/coin_addresses_state.dart b/lib/bloc/coin_addresses/bloc/coin_addresses_state.dart index fa430db51e..d5313af190 100644 --- a/lib/bloc/coin_addresses/bloc/coin_addresses_state.dart +++ b/lib/bloc/coin_addresses/bloc/coin_addresses_state.dart @@ -10,6 +10,7 @@ class CoinAddressesState extends Equatable { final List addresses; final bool hideZeroBalance; final Set? cantCreateNewAddressReasons; + final NewAddressState? newAddressState; const CoinAddressesState({ this.status = FormStatus.initial, @@ -18,6 +19,7 @@ class CoinAddressesState extends Equatable { this.addresses = const [], this.hideZeroBalance = false, this.cantCreateNewAddressReasons, + this.newAddressState, }); CoinAddressesState copyWith({ @@ -27,6 +29,7 @@ class CoinAddressesState extends Equatable { List Function()? addresses, bool Function()? hideZeroBalance, Set? Function()? cantCreateNewAddressReasons, + NewAddressState? Function()? newAddressState, }) { return CoinAddressesState( status: status == null ? this.status : status(), @@ -40,6 +43,8 @@ class CoinAddressesState extends Equatable { cantCreateNewAddressReasons: cantCreateNewAddressReasons == null ? this.cantCreateNewAddressReasons : cantCreateNewAddressReasons(), + newAddressState: + newAddressState == null ? this.newAddressState : newAddressState(), ); } @@ -50,6 +55,7 @@ class CoinAddressesState extends Equatable { List Function()? addresses, bool Function()? hideZeroBalance, Set? Function()? cantCreateNewAddressReasons, + NewAddressState? Function()? newAddressState, }) { return CoinAddressesState( status: status == null ? FormStatus.initial : status(), @@ -62,6 +68,7 @@ class CoinAddressesState extends Equatable { cantCreateNewAddressReasons: cantCreateNewAddressReasons == null ? null : cantCreateNewAddressReasons(), + newAddressState: newAddressState == null ? null : newAddressState(), ); } @@ -73,5 +80,6 @@ class CoinAddressesState extends Equatable { addresses, hideZeroBalance, cantCreateNewAddressReasons, + newAddressState, ]; } diff --git a/lib/bloc/coins_bloc/coins_bloc.dart b/lib/bloc/coins_bloc/coins_bloc.dart index cdadfc0332..a8a77347f0 100644 --- a/lib/bloc/coins_bloc/coins_bloc.dart +++ b/lib/bloc/coins_bloc/coins_bloc.dart @@ -11,7 +11,6 @@ import 'package:logging/logging.dart'; import 'package:web_dex/app_config/app_config.dart'; import 'package:web_dex/bloc/coins_bloc/asset_coin_extension.dart'; import 'package:web_dex/bloc/coins_bloc/coins_repo.dart'; -import 'package:web_dex/blocs/trezor_coins_bloc.dart'; import 'package:web_dex/mm2/mm2_api/mm2_api.dart'; import 'package:web_dex/model/cex_price.dart'; import 'package:web_dex/model/coin.dart'; @@ -27,7 +26,6 @@ class CoinsBloc extends Bloc { CoinsBloc( this._kdfSdk, this._coinsRepo, - this._trezorBloc, this._mm2Api, ) : super(CoinsState.initial()) { on(_onCoinsStarted, transformer: droppable()); @@ -54,9 +52,6 @@ class CoinsBloc extends Bloc { final KomodoDefiSdk _kdfSdk; final CoinsRepo _coinsRepo; final Mm2Api _mm2Api; - // TODO: refactor to use repository - pin/password input events need to be - // handled, which are currently done through the trezor "bloc" - final TrezorCoinsBloc _trezorBloc; final _log = Logger('CoinsBloc'); @@ -130,15 +125,6 @@ class CoinsBloc extends Bloc { final currentWallet = await _kdfSdk.currentWallet(); switch (currentWallet?.config.type) { case WalletType.trezor: - final walletCoins = - await _coinsRepo.updateTrezorBalances(state.walletCoins); - emit( - state.copyWith( - walletCoins: walletCoins, - // update balances in all coins list as well - coins: {...state.coins, ...walletCoins}, - ), - ); case WalletType.metamask: case WalletType.keplr: case WalletType.iguana: @@ -392,8 +378,9 @@ class CoinsBloc extends Bloc { } final enabledAssets = await _kdfSdk.assets.getEnabledCoins(); - final coinsToActivate = - coins.where((coin) => !enabledAssets.contains(coin)); + final coinsToActivate = coins + .where((coin) => !enabledAssets.contains(coin)) + .where((coin) => _coinsRepo.getKnownCoinsMap().containsKey(coin)); final enableFutures = coinsToActivate.map((coin) => _activateCoin(coin)).toList(); @@ -415,11 +402,12 @@ class CoinsBloc extends Bloc { Future _prePopulateListWithActivatingCoins( Iterable coins) async { final currentWallet = await _kdfSdk.currentWallet(); + final knownCoins = _coinsRepo.getKnownCoinsMap(); final activatingCoins = Map.fromIterable( coins .map( (coin) { - final sdkCoin = state.coins[coin] ?? _coinsRepo.getCoin(coin); + final sdkCoin = knownCoins[coin]; return sdkCoin?.copyWith( state: CoinState.activating, enabledType: currentWallet?.config.type, @@ -432,7 +420,7 @@ class CoinsBloc extends Bloc { ); return state.copyWith( walletCoins: {...state.walletCoins, ...activatingCoins}, - coins: {...state.coins, ...activatingCoins}, + coins: {...knownCoins, ...state.coins, ...activatingCoins}, ); } @@ -452,9 +440,8 @@ class CoinsBloc extends Bloc { switch (currentWallet.config.type) { case WalletType.iguana: case WalletType.hdwallet: - coin = await _activateIguanaCoin(coin); case WalletType.trezor: - coin = await _activateTrezorCoin(coin, coinId); + coin = await _activateIguanaCoin(coin); case WalletType.metamask: case WalletType.keplr: } @@ -465,17 +452,6 @@ class CoinsBloc extends Bloc { return coin; } - Future _activateTrezorCoin(Coin coin, String coinId) async { - final asset = _kdfSdk.assets.available[coin.id]; - if (asset == null) { - _log.severe('Failed to find asset for coin: ${coin.id}'); - return coin.copyWith(state: CoinState.suspended); - } - final accounts = await _trezorBloc.activateCoin(asset); - final state = accounts.isNotEmpty ? CoinState.active : CoinState.suspended; - return coin.copyWith(state: state, accounts: accounts); - } - Future _activateIguanaCoin(Coin coin) async { try { _log.info('Enabling iguana coin: ${coin.id.id}'); diff --git a/lib/bloc/coins_bloc/coins_repo.dart b/lib/bloc/coins_bloc/coins_repo.dart index bfc7f1a7b4..078c620928 100644 --- a/lib/bloc/coins_bloc/coins_repo.dart +++ b/lib/bloc/coins_bloc/coins_repo.dart @@ -13,7 +13,6 @@ import 'package:komodo_ui/komodo_ui.dart'; import 'package:logging/logging.dart'; import 'package:web_dex/app_config/app_config.dart' show excludedAssetList; import 'package:web_dex/bloc/coins_bloc/asset_coin_extension.dart'; -import 'package:web_dex/blocs/trezor_coins_bloc.dart'; import 'package:web_dex/generated/codegen_loader.g.dart'; import 'package:web_dex/mm2/mm2.dart'; import 'package:web_dex/mm2/mm2_api/rpc/base.dart'; @@ -32,10 +31,8 @@ class CoinsRepo { CoinsRepo({ required KomodoDefiSdk kdfSdk, required MM2 mm2, - required TrezorCoinsBloc trezorBloc, }) : _kdfSdk = kdfSdk, - _mm2 = mm2, - trezor = trezorBloc { + _mm2 = mm2 { enabledAssetsChanges = StreamController.broadcast( onListen: () => _enabledAssetListenerCount += 1, onCancel: () => _enabledAssetListenerCount -= 1, @@ -44,9 +41,6 @@ class CoinsRepo { final KomodoDefiSdk _kdfSdk; final MM2 _mm2; - // TODO: refactor to use repository - pin/password input events need to be - // handled, which are currently done through the trezor "bloc" - final TrezorCoinsBloc trezor; final _log = Logger('CoinsRepo'); @@ -563,19 +557,7 @@ class CoinsRepo { return prices; } - Future> updateTrezorBalances( - Map walletCoins, - ) async { - final walletCoinsCopy = Map.from(walletCoins); - final coins = - walletCoinsCopy.entries.where((entry) => entry.value.isActive).toList(); - for (final MapEntry entry in coins) { - walletCoinsCopy[entry.key]!.accounts = - await trezor.trezorRepo.getAccounts(entry.value); - } - - return walletCoinsCopy; - } + // updateTrezorBalances removed (TrezorRepo deleted) /// Updates balances for active coins by querying the SDK /// Yields coins that have balance changes diff --git a/lib/bloc/coins_manager/coins_manager_bloc.dart b/lib/bloc/coins_manager/coins_manager_bloc.dart index 9d6d98dbe2..fe66f5a722 100644 --- a/lib/bloc/coins_manager/coins_manager_bloc.dart +++ b/lib/bloc/coins_manager/coins_manager_bloc.dart @@ -271,12 +271,9 @@ Future> _getDeactivatedCoins( switch (walletType) { case WalletType.iguana: + case WalletType.trezor: case WalletType.hdwallet: return disabledCoins.values.toList(); - case WalletType.trezor: - final disabledCoinsWithTrezorSupport = - disabledCoins.values.where((coin) => coin.hasTrezorSupport); - return disabledCoinsWithTrezorSupport.toList(); case WalletType.metamask: case WalletType.keplr: return []; diff --git a/lib/bloc/trezor_bloc/trezor_repo.dart b/lib/bloc/trezor_bloc/trezor_repo.dart deleted file mode 100644 index 81f5b6955f..0000000000 --- a/lib/bloc/trezor_bloc/trezor_repo.dart +++ /dev/null @@ -1,192 +0,0 @@ -import 'dart:async'; - -import 'package:komodo_defi_sdk/komodo_defi_sdk.dart'; -import 'package:komodo_defi_types/komodo_defi_types.dart'; -import 'package:web_dex/mm2/mm2_api/mm2_api_trezor.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/balance/trezor_balance_init/trezor_balance_init_request.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/balance/trezor_balance_init/trezor_balance_init_response.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/balance/trezor_balance_status/trezor_balance_status_request.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/balance/trezor_balance_status/trezor_balance_status_response.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/enable_utxo/trezor_enable_utxo/trezor_enable_utxo_request.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/enable_utxo/trezor_enable_utxo/trezor_enable_utxo_response.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/enable_utxo/trezor_enable_utxo_status/trezor_enable_utxo_status_request.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/enable_utxo/trezor_enable_utxo_status/trezor_enable_utxo_status_response.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/get_new_address/get_new_address_response.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/init/init_trezor/init_trezor_request.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/init/init_trezor/init_trezor_response.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/init/init_trezor_cancel/init_trezor_cancel_request.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/init/init_trezor_status/init_trezor_status_request.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/init/init_trezor_status/init_trezor_status_response.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/trezor_passphrase/trezor_passphrase_request.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/trezor_pin/trezor_pin_request.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/withdraw/trezor_withdraw/trezor_withdraw_request.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/withdraw/trezor_withdraw/trezor_withdraw_response.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/withdraw/trezor_withdraw_cancel/trezor_withdraw_cancel_request.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/withdraw/trezor_withdraw_status/trezor_withdraw_status_request.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/withdraw/trezor_withdraw_status/trezor_withdraw_status_response.dart'; -import 'package:web_dex/model/coin.dart'; -import 'package:web_dex/model/hd_account/hd_account.dart'; -import 'package:web_dex/model/hw_wallet/trezor_connection_status.dart'; -import 'package:web_dex/model/hw_wallet/trezor_status.dart'; -import 'package:web_dex/model/hw_wallet/trezor_task.dart'; -import 'package:web_dex/model/kdf_auth_metadata_extension.dart'; -import 'package:web_dex/model/wallet.dart'; -import 'package:web_dex/shared/utils/utils.dart'; - -class TrezorRepo { - TrezorRepo({ - required Mm2ApiTrezor api, - required KomodoDefiSdk kdfSdk, - }) : _api = api, - _kdfSdk = kdfSdk; - - final Mm2ApiTrezor _api; - final KomodoDefiSdk _kdfSdk; - - Future init() async { - return await _api.init(InitTrezorReq()); - } - - final StreamController _connectionStatusController = - StreamController.broadcast(); - Stream get connectionStatusStream => - _connectionStatusController.stream; - Timer? _connectionStatusTimer; - - Future initStatus(int taskId) async { - return await _api.initStatus(InitTrezorStatusReq(taskId: taskId)); - } - - Future sendPin(String pin, TrezorTask trezorTask) async { - await _api.pin( - TrezorPinRequest( - pin: pin, - task: trezorTask, - ), - ); - } - - Future sendPassphrase(String passphrase, TrezorTask trezorTask) async { - await _api.passphrase( - TrezorPassphraseRequest( - passphrase: passphrase, - task: trezorTask, - ), - ); - } - - Future initCancel(int taskId) async { - await _api.initCancel(InitTrezorCancelReq(taskId: taskId)); - } - - Future initBalance(Coin coin) async { - return await _api.balanceInit(TrezorBalanceInitRequest(coin: coin)); - } - - Future getBalanceStatus(int taskId) async { - return await _api.balanceStatus(TrezorBalanceStatusRequest(taskId: taskId)); - } - - Future enableUtxo(Asset asset) async { - return await _api.enableUtxo(TrezorEnableUtxoReq(coin: asset)); - } - - Future getEnableUtxoStatus(int taskId) async { - return await _api - .enableUtxoStatus(TrezorEnableUtxoStatusReq(taskId: taskId)); - } - - Future initNewAddress(String coin) async { - return await _api.initNewAddress(coin); - } - - Future cancelGetNewAddress(int taskId) async { - await _api.cancelGetNewAddress(taskId); - } - - Future withdraw(TrezorWithdrawRequest request) async { - return await _api.withdraw(request); - } - - Future getWithdrawStatus(int taskId) async { - return _api.withdrawStatus(TrezorWithdrawStatusRequest(taskId: taskId)); - } - - Future cancelWithdraw(int taskId) async { - await _api.withdrawCancel(TrezorWithdrawCancelRequest(taskId: taskId)); - } - - void subscribeOnConnectionStatus(String pubKey) { - if (_connectionStatusTimer != null) return; - _connectionStatusTimer = - Timer.periodic(const Duration(seconds: 1), (timer) async { - final TrezorConnectionStatus status = - await _api.getConnectionStatus(pubKey); - _connectionStatusController.sink.add(status); - if (status == TrezorConnectionStatus.unreachable) { - _connectionStatusTimer?.cancel(); - _connectionStatusTimer = null; - } - }); - } - - void unsubscribeFromConnectionStatus() { - if (_connectionStatusTimer == null) return; - _connectionStatusTimer?.cancel(); - _connectionStatusTimer = null; - } - - Future isTrezorWallet() async { - final currentWallet = await _kdfSdk.currentWallet(); - return currentWallet?.config.type == WalletType.trezor; - } - - Future?> getAccounts(Coin coin) async { - final TrezorBalanceInitResponse initResponse = - await _api.balanceInit(TrezorBalanceInitRequest(coin: coin)); - final int? taskId = initResponse.result?.taskId; - if (taskId == null) return null; - - final int started = nowMs; - // todo(yurii): change timeout to some reasonable value (10000?) - while (nowMs - started < 100000) { - final statusResponse = - await _api.balanceStatus(TrezorBalanceStatusRequest(taskId: taskId)); - final InitTrezorStatus? status = statusResponse.result?.status; - - if (status == InitTrezorStatus.error) return null; - - if (status == InitTrezorStatus.ok) { - return statusResponse.result?.balanceDetails?.accounts; - } - - await Future.delayed(const Duration(milliseconds: 500)); - } - - return null; - } - - Future getNewAddressStatus( - int taskId, - Asset asset, - ) async { - final GetNewAddressResponse response = - await _api.getNewAddressStatus(taskId); - - // TODO: migrate to the SDK along with trezor repo - // This is also done on the periodic balance update polling in [CoinsBloc] - // in [updateTrezorBalances] so this being commented out might result in - // a delay to balance updates but it would require streaming updates to - // the coins bloc or sdk to update balances that are being migrated to - // the SDK stream-based approach. - // - // final GetNewAddressStatus? status = response.result?.status; - // final GetNewAddressResultDetails? details = response.result?.details; - // if (status == GetNewAddressStatus.ok && - // details is GetNewAddressResultOkDetails) { - // coin.accounts = await getAccounts(coin); - // } - - return response; - } -} diff --git a/lib/bloc/trezor_connection_bloc/trezor_connection_bloc.dart b/lib/bloc/trezor_connection_bloc/trezor_connection_bloc.dart deleted file mode 100644 index 128d6c56e1..0000000000 --- a/lib/bloc/trezor_connection_bloc/trezor_connection_bloc.dart +++ /dev/null @@ -1,72 +0,0 @@ -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'; -import 'package:web_dex/bloc/trezor_bloc/trezor_repo.dart'; -import 'package:web_dex/bloc/trezor_connection_bloc/trezor_connection_event.dart'; -import 'package:web_dex/bloc/trezor_connection_bloc/trezor_connection_state.dart'; -import 'package:web_dex/model/hw_wallet/trezor_connection_status.dart'; -import 'package:web_dex/model/wallet.dart'; - -class TrezorConnectionBloc - extends Bloc { - TrezorConnectionBloc({ - required TrezorRepo trezorRepo, - required KomodoDefiSdk kdfSdk, - }) : _kdfSdk = kdfSdk, - _trezorRepo = trezorRepo, - super(TrezorConnectionState.initial()) { - _trezorConnectionStatusListener = trezorRepo.connectionStatusStream - .listen(_onTrezorConnectionStatusChanged); - _authModeListener = - kdfSdk.auth.watchCurrentUser().listen(_onAuthModeChanged); - - on(_onConnectionStatusChange); - } - - void _onTrezorConnectionStatusChanged(TrezorConnectionStatus status) { - add(TrezorConnectionStatusChange(status: status)); - } - - void _onAuthModeChanged(KdfUser? user) { - if (user == null) { - return _trezorRepo.unsubscribeFromConnectionStatus(); - } - - if (user.wallet.config.type != WalletType.trezor) return; - - final String? pubKey = user.walletId.pubkeyHash; - if (pubKey == null) return; - - _trezorRepo.subscribeOnConnectionStatus(pubKey); - } - - final KomodoDefiSdk _kdfSdk; - final TrezorRepo _trezorRepo; - late StreamSubscription - _trezorConnectionStatusListener; - late StreamSubscription _authModeListener; - - Future _onConnectionStatusChange(TrezorConnectionStatusChange event, - Emitter emit) async { - final status = event.status; - emit(TrezorConnectionState(status: status)); - - switch (status) { - case TrezorConnectionStatus.unreachable: - await _kdfSdk.auth.signOut(); - return; - case TrezorConnectionStatus.unknown: - case TrezorConnectionStatus.connected: - return; - } - } - - @override - Future close() { - _trezorConnectionStatusListener.cancel(); - _authModeListener.cancel(); - return super.close(); - } -} diff --git a/lib/bloc/trezor_connection_bloc/trezor_connection_event.dart b/lib/bloc/trezor_connection_bloc/trezor_connection_event.dart deleted file mode 100644 index 9c4864be01..0000000000 --- a/lib/bloc/trezor_connection_bloc/trezor_connection_event.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'package:web_dex/model/hw_wallet/trezor_connection_status.dart'; - -abstract class TrezorConnectionEvent { - const TrezorConnectionEvent(); -} - -class TrezorConnectionStatusChange extends TrezorConnectionEvent { - const TrezorConnectionStatusChange({required this.status}); - final TrezorConnectionStatus status; -} diff --git a/lib/bloc/trezor_connection_bloc/trezor_connection_state.dart b/lib/bloc/trezor_connection_bloc/trezor_connection_state.dart deleted file mode 100644 index 26410a4459..0000000000 --- a/lib/bloc/trezor_connection_bloc/trezor_connection_state.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:equatable/equatable.dart'; -import 'package:web_dex/model/hw_wallet/trezor_connection_status.dart'; - -class TrezorConnectionState extends Equatable { - const TrezorConnectionState({required this.status}); - final TrezorConnectionStatus status; - - static TrezorConnectionState initial() => - const TrezorConnectionState(status: TrezorConnectionStatus.unknown); - - @override - List get props => [status]; -} diff --git a/lib/bloc/trezor_init_bloc/trezor_init_bloc.dart b/lib/bloc/trezor_init_bloc/trezor_init_bloc.dart deleted file mode 100644 index a13ec6358d..0000000000 --- a/lib/bloc/trezor_init_bloc/trezor_init_bloc.dart +++ /dev/null @@ -1,372 +0,0 @@ -import 'dart:async'; -import 'package:easy_localization/easy_localization.dart'; -import 'package:equatable/equatable.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; -import 'package:komodo_defi_sdk/komodo_defi_sdk.dart'; -import 'package:komodo_defi_types/komodo_defi_types.dart'; -import 'package:web_dex/app_config/app_config.dart'; -import 'package:web_dex/shared/utils/password.dart'; -import 'package:web_dex/bloc/coins_bloc/coins_repo.dart'; -import 'package:web_dex/bloc/trezor_bloc/trezor_repo.dart'; -import 'package:web_dex/generated/codegen_loader.g.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/init/init_trezor/init_trezor_response.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/init/init_trezor_status/init_trezor_status_response.dart'; -import 'package:web_dex/model/hw_wallet/init_trezor.dart'; -import 'package:web_dex/model/hw_wallet/trezor_status.dart'; -import 'package:web_dex/model/hw_wallet/trezor_status_error.dart'; -import 'package:web_dex/model/hw_wallet/trezor_task.dart'; -import 'package:web_dex/model/kdf_auth_metadata_extension.dart'; -import 'package:web_dex/model/text_error.dart'; -import 'package:web_dex/model/wallet.dart'; -import 'package:web_dex/shared/utils/utils.dart'; -import 'package:komodo_defi_rpc_methods/komodo_defi_rpc_methods.dart' - show PrivateKeyPolicy; - -part 'trezor_init_event.dart'; -part 'trezor_init_state.dart'; - -const String _trezorPasswordKey = 'trezor_wallet_password'; - -class TrezorInitBloc extends Bloc { - TrezorInitBloc({ - required KomodoDefiSdk kdfSdk, - required TrezorRepo trezorRepo, - required CoinsRepo coinsRepository, - FlutterSecureStorage? secureStorage, - }) : _trezorRepo = trezorRepo, - _kdfSdk = kdfSdk, - _coinsRepository = coinsRepository, - _secureStorage = secureStorage ?? const FlutterSecureStorage(), - super(TrezorInitState.initial()) { - on(_onSubscribeStatus); - on(_onInit); - on(_onReset); - on(_onUpdateStatus); - on(_onInitSuccess); - on(_onSendPin); - on(_onSendPassphrase); - on(_onAuthModeChange); - - _authorizationSubscription = _kdfSdk.auth.watchCurrentUser().listen((user) { - add(TrezorInitUpdateAuthMode(user)); - }); - } - - late StreamSubscription _authorizationSubscription; - final TrezorRepo _trezorRepo; - final KomodoDefiSdk _kdfSdk; - final CoinsRepo _coinsRepository; - final FlutterSecureStorage _secureStorage; - Timer? _statusTimer; - - void _unsubscribeStatus() { - _statusTimer?.cancel(); - _statusTimer = null; - } - - bool _checkAndHandleSuccess(InitTrezorStatusData status) { - final InitTrezorStatus trezorStatus = status.trezorStatus; - final TrezorDeviceDetails? deviceDetails = status.details.deviceDetails; - - if (trezorStatus == InitTrezorStatus.ok && deviceDetails != null) { - add(TrezorInitSuccess(status)); - return true; - } - - return false; - } - - Future _onInit(TrezorInit event, Emitter emit) async { - if (state.inProgress) return; - emit(state.copyWith(inProgress: () => true)); - try { - // device status isn't available until after trezor init completes, but - // requires kdf to be running with a seed value. - // Alternative is to use a static 'hidden-login' to init trezor, then logout - // and log back in to another account using the obtained trezor device - // details - await _loginToTrezorWallet(); - } catch (e, s) { - log( - 'Failed to login to hidden mode: $e', - path: 'trezor_init_bloc => _loginToHiddenMode', - isError: true, - trace: s, - ).ignore(); - emit( - state.copyWith( - error: () => TextError(error: LocaleKeys.somethingWrong.tr()), - inProgress: () => false, - ), - ); - return; - } - - final InitTrezorRes response = await _trezorRepo.init(); - final String? responseError = response.error; - final InitTrezorResult? responseResult = response.result; - - if (responseError != null) { - emit( - state.copyWith( - error: () => TextError(error: responseError), - inProgress: () => false, - ), - ); - await _logout(); - return; - } - if (responseResult == null) { - emit( - state.copyWith( - error: () => TextError(error: LocaleKeys.somethingWrong.tr()), - inProgress: () => false, - ), - ); - await _logout(); - return; - } - - add(const TrezorInitSubscribeStatus()); - emit( - state.copyWith( - taskId: () => responseResult.taskId, - inProgress: () => false, - ), - ); - } - - Future _onSubscribeStatus( - TrezorInitSubscribeStatus event, - Emitter emit, - ) async { - _statusTimer = Timer.periodic(const Duration(milliseconds: 1000), (timer) { - add(const TrezorInitUpdateStatus()); - }); - } - - FutureOr _onUpdateStatus( - TrezorInitUpdateStatus event, - Emitter emit, - ) async { - final int? taskId = state.taskId; - if (taskId == null) return; - - final InitTrezorStatusRes response = await _trezorRepo.initStatus(taskId); - - if (response.errorType == 'NoSuchTask') { - _unsubscribeStatus(); - emit(state.copyWith(taskId: () => null)); - await _logout(); - return; - } - - final String? responseError = response.error; - - if (responseError != null) { - emit(state.copyWith(error: () => TextError(error: responseError))); - await _logout(); - return; - } - - final InitTrezorStatusData? initTrezorStatus = response.result; - if (initTrezorStatus == null) { - emit( - state.copyWith( - error: () => - TextError(error: 'Something went wrong. Empty init status.'), - ), - ); - - await _logout(); - return; - } - - if (!_checkAndHandleSuccess(initTrezorStatus)) { - emit(state.copyWith(status: () => initTrezorStatus)); - } - - if (_checkAndHandleInvalidPin(initTrezorStatus)) { - emit(state.copyWith(taskId: () => null)); - _unsubscribeStatus(); - } - } - - Future _onInitSuccess( - TrezorInitSuccess event, - Emitter emit, - ) async { - _unsubscribeStatus(); - final deviceDetails = event.status.details.deviceDetails!; - - // final String name = deviceDetails.name ?? 'My Trezor'; - - try { - await _coinsRepository - .deactivateCoinsSync(await _coinsRepository.getEnabledCoins()); - } catch (e) { - // ignore - } - _trezorRepo.subscribeOnConnectionStatus(deviceDetails.pubKey); - emit( - state.copyWith( - inProgress: () => false, - kdfUser: await _kdfSdk.auth.currentUser, - status: () => event.status, - ), - ); - } - - Future _onSendPin( - TrezorInitSendPin event, - Emitter emit, - ) async { - final int? taskId = state.taskId; - - if (taskId == null) return; - await _trezorRepo.sendPin( - event.pin, - TrezorTask( - taskId: taskId, - type: TrezorTaskType.initTrezor, - ), - ); - } - - Future _onSendPassphrase( - TrezorInitSendPassphrase event, - Emitter emit, - ) async { - final int? taskId = state.taskId; - - if (taskId == null) return; - - await _trezorRepo.sendPassphrase( - event.passphrase, - TrezorTask( - taskId: taskId, - type: TrezorTaskType.initTrezor, - ), - ); - } - - FutureOr _onReset( - TrezorInitReset event, - Emitter emit, - ) async { - _unsubscribeStatus(); - final taskId = state.taskId; - - if (taskId != null) { - await _trezorRepo.initCancel(taskId); - } - _logout(); - emit( - state.copyWith( - taskId: () => null, - status: () => null, - error: () => null, - ), - ); - } - - FutureOr _onAuthModeChange( - TrezorInitUpdateAuthMode event, - Emitter emit, - ) { - emit(state.copyWith(kdfUser: event.kdfUser)); - } - - /// KDF has to be running with a seed/wallet to init a trezor, so this signs - /// into a static 'hidden' wallet to init trezor - Future _loginToTrezorWallet({ - String walletName = 'My Trezor', - String? password, - AuthOptions authOptions = const AuthOptions( - derivationMethod: DerivationMethod.hdWallet, - privKeyPolicy: PrivateKeyPolicy.trezor(), - ), - }) async { - try { - password ??= await _secureStorage.read(key: _trezorPasswordKey); - } catch (e, s) { - log( - 'Failed to read trezor password from secure storage: $e', - path: 'trezor_init_bloc => _loginToTrezorWallet', - isError: true, - trace: s, - ).ignore(); - // If reading fails, password will remain null and a new one will be generated - } - - if (password == null) { - password = generatePassword(); - try { - await _secureStorage.write(key: _trezorPasswordKey, value: password); - } catch (e, s) { - log( - 'Failed to write trezor password to secure storage: $e', - path: 'trezor_init_bloc => _loginToTrezorWallet', - isError: true, - trace: s, - ).ignore(); - // Continue with generated password even if storage write fails - } - } - - final bool mm2SignedIn = await _kdfSdk.auth.isSignedIn(); - if (state.kdfUser != null && mm2SignedIn) { - return; - } - - final existingWallets = await _kdfSdk.auth.getUsers(); - if (existingWallets.any((wallet) => wallet.walletId.name == walletName)) { - await _kdfSdk.auth.signIn( - walletName: walletName, - password: password, - options: authOptions, - ); - await _kdfSdk.setWalletType(WalletType.trezor); - await _kdfSdk.confirmSeedBackup(); - await _kdfSdk.addActivatedCoins(enabledByDefaultTrezorCoins); - return; - } - - await _kdfSdk.auth.register( - walletName: walletName, - password: password, - options: authOptions, - ); - await _kdfSdk.setWalletType(WalletType.trezor); - await _kdfSdk.confirmSeedBackup(); - await _kdfSdk.addActivatedCoins(enabledByDefaultTrezorCoins); - } - - Future _logout() async { - final bool isSignedIn = await _kdfSdk.auth.isSignedIn(); - if (!isSignedIn && state.kdfUser == null) { - return; - } - - await _kdfSdk.auth.signOut(); - } - - bool _checkAndHandleInvalidPin(InitTrezorStatusData status) { - if (status.trezorStatus != InitTrezorStatus.error) return false; - if (status.details.errorDetails == null) return false; - if (status.details.errorDetails!.errorData != - TrezorStatusErrorData.invalidPin) { - return false; - } - - return true; - } - - @override - Future close() async { - _unsubscribeStatus(); - await _authorizationSubscription.cancel(); - return super.close(); - } -} diff --git a/lib/bloc/trezor_init_bloc/trezor_init_event.dart b/lib/bloc/trezor_init_bloc/trezor_init_event.dart deleted file mode 100644 index b018354e33..0000000000 --- a/lib/bloc/trezor_init_bloc/trezor_init_event.dart +++ /dev/null @@ -1,42 +0,0 @@ -part of 'trezor_init_bloc.dart'; - -abstract class TrezorInitEvent { - const TrezorInitEvent(); -} - -class TrezorInitUpdateAuthMode extends TrezorInitEvent { - const TrezorInitUpdateAuthMode(this.kdfUser); - final KdfUser? kdfUser; -} - -class TrezorInit extends TrezorInitEvent { - const TrezorInit(); -} - -class TrezorInitReset extends TrezorInitEvent { - const TrezorInitReset(); -} - -class TrezorInitSubscribeStatus extends TrezorInitEvent { - const TrezorInitSubscribeStatus(); -} - -class TrezorInitUpdateStatus extends TrezorInitEvent { - const TrezorInitUpdateStatus(); -} - -class TrezorInitSuccess extends TrezorInitEvent { - const TrezorInitSuccess(this.status); - - final InitTrezorStatusData status; -} - -class TrezorInitSendPin extends TrezorInitEvent { - const TrezorInitSendPin(this.pin); - final String pin; -} - -class TrezorInitSendPassphrase extends TrezorInitEvent { - const TrezorInitSendPassphrase(this.passphrase); - final String passphrase; -} diff --git a/lib/bloc/trezor_init_bloc/trezor_init_state.dart b/lib/bloc/trezor_init_bloc/trezor_init_state.dart deleted file mode 100644 index d909ba6616..0000000000 --- a/lib/bloc/trezor_init_bloc/trezor_init_state.dart +++ /dev/null @@ -1,42 +0,0 @@ -part of 'trezor_init_bloc.dart'; - -class TrezorInitState extends Equatable { - const TrezorInitState({ - required this.taskId, - required this.status, - this.kdfUser, - this.error, - required this.inProgress, - }); - static TrezorInitState initial() => const TrezorInitState( - taskId: null, - kdfUser: null, - error: null, - status: null, - inProgress: false, - ); - - TrezorInitState copyWith({ - int? Function()? taskId, - KdfUser? kdfUser, - TextError? Function()? error, - InitTrezorStatusData? Function()? status, - bool Function()? inProgress, - }) => - TrezorInitState( - taskId: taskId != null ? taskId() : this.taskId, - kdfUser: kdfUser ?? this.kdfUser, - status: status != null ? status() : this.status, - error: error != null ? error() : this.error, - inProgress: inProgress != null ? inProgress() : this.inProgress, - ); - - final int? taskId; - final InitTrezorStatusData? status; - final TextError? error; - final bool inProgress; - final KdfUser? kdfUser; - - @override - List get props => [taskId, kdfUser, status, error, inProgress]; -} diff --git a/lib/blocs/trezor_coins_bloc.dart b/lib/blocs/trezor_coins_bloc.dart deleted file mode 100644 index 08468b0cbe..0000000000 --- a/lib/blocs/trezor_coins_bloc.dart +++ /dev/null @@ -1,194 +0,0 @@ -import 'dart:async'; - -import 'package:easy_localization/easy_localization.dart'; -import 'package:komodo_defi_types/komodo_defi_types.dart'; -import 'package:web_dex/bloc/trezor_bloc/trezor_repo.dart'; -import 'package:web_dex/generated/codegen_loader.g.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/base.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/bloc_response.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/get_new_address/get_new_address_response.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/withdraw/trezor_withdraw/trezor_withdraw_request.dart'; -import 'package:web_dex/model/hd_account/hd_account.dart'; -import 'package:web_dex/model/hw_wallet/init_trezor.dart'; -import 'package:web_dex/model/hw_wallet/trezor_progress_status.dart'; -import 'package:web_dex/model/hw_wallet/trezor_status.dart'; -import 'package:web_dex/model/hw_wallet/trezor_task.dart'; -import 'package:web_dex/model/text_error.dart'; -import 'package:web_dex/model/withdraw_details/withdraw_details.dart'; -import 'package:web_dex/shared/utils/utils.dart'; -import 'package:web_dex/views/common/hw_wallet_dialog/show_trezor_passphrase_dialog.dart'; -import 'package:web_dex/views/common/hw_wallet_dialog/show_trezor_pin_dialog.dart'; - -class TrezorCoinsBloc { - TrezorCoinsBloc({ - required this.trezorRepo, - }); - - final TrezorRepo trezorRepo; - Timer? _initNewAddressStatusTimer; - - Future initNewAddress(Asset asset) async { - final TrezorGetNewAddressInitResponse response = - await trezorRepo.initNewAddress(asset.id.id); - final result = response.result; - - return result?.taskId; - } - - void subscribeOnNewAddressStatus( - int taskId, - Asset asset, - void Function(GetNewAddressResponse) callback, - ) { - _initNewAddressStatusTimer = - Timer.periodic(const Duration(seconds: 1), (timer) async { - final GetNewAddressResponse initNewAddressStatus = - await trezorRepo.getNewAddressStatus(taskId, asset); - callback(initNewAddressStatus); - }); - } - - void unsubscribeFromNewAddressStatus() { - _initNewAddressStatusTimer?.cancel(); - _initNewAddressStatusTimer = null; - } - - Future> activateCoin(Asset asset) async { - switch (asset.id.subClass) { - case CoinSubClass.utxo: - case CoinSubClass.smartChain: - return await _enableUtxo(asset); - default: - return List.empty(); - } - } - - Future> _enableUtxo(Asset asset) async { - final enableResponse = await trezorRepo.enableUtxo(asset); - final taskId = enableResponse.result?.taskId; - if (taskId == null) return List.empty(); - - while (await trezorRepo.isTrezorWallet()) { - final statusResponse = await trezorRepo.getEnableUtxoStatus(taskId); - final InitTrezorStatus? status = statusResponse.result?.status; - - switch (status) { - case InitTrezorStatus.error: - return List.empty(); - - case InitTrezorStatus.userActionRequired: - final TrezorUserAction? action = statusResponse.result?.actionDetails; - if (action == TrezorUserAction.enterTrezorPin) { - // TODO! :( - await showTrezorPinDialog(TrezorTask( - taskId: taskId, - type: TrezorTaskType.enableUtxo, - )); - } else if (action == TrezorUserAction.enterTrezorPassphrase) { - // TODO! :( - await showTrezorPassphraseDialog(TrezorTask( - taskId: taskId, - type: TrezorTaskType.enableUtxo, - )); - } - break; - - case InitTrezorStatus.ok: - final details = statusResponse.result?.details; - if (details != null) { - return details.accounts; - } - - default: - } - - await Future.delayed(const Duration(milliseconds: 500)); - } - - return List.empty(); - } - - Future> withdraw( - TrezorWithdrawRequest request, { - required void Function(TrezorProgressStatus?) onProgressUpdated, - }) async { - final withdrawResponse = await trezorRepo.withdraw(request); - - if (withdrawResponse.error != null) { - return BlocResponse( - result: null, - error: TextError(error: withdrawResponse.error!), - ); - } - - final int? taskId = withdrawResponse.result?.taskId; - if (taskId == null) { - return BlocResponse( - result: null, - error: TextError(error: LocaleKeys.somethingWrong.tr()), - ); - } - - final int started = nowMs; - while (nowMs - started < 1000 * 60 * 3) { - final statusResponse = await trezorRepo.getWithdrawStatus(taskId); - - if (statusResponse.error != null) { - return BlocResponse( - result: null, - error: TextError(error: statusResponse.error!), - ); - } - - final InitTrezorStatus? status = statusResponse.result?.status; - - switch (status) { - case InitTrezorStatus.error: - return BlocResponse( - result: null, - error: TextError( - error: statusResponse.result?.errorDetails?.error ?? - LocaleKeys.somethingWrong.tr()), - ); - - case InitTrezorStatus.inProgress: - final TrezorProgressStatus? progressDetails = - statusResponse.result?.progressDetails; - - onProgressUpdated(progressDetails); - break; - - case InitTrezorStatus.userActionRequired: - final TrezorUserAction? action = statusResponse.result?.actionDetails; - if (action == TrezorUserAction.enterTrezorPin) { - await showTrezorPinDialog(TrezorTask( - taskId: taskId, - type: TrezorTaskType.withdraw, - )); - } else if (action == TrezorUserAction.enterTrezorPassphrase) { - await showTrezorPassphraseDialog(TrezorTask( - taskId: taskId, - type: TrezorTaskType.enableUtxo, - )); - } - break; - - case InitTrezorStatus.ok: - return BlocResponse( - result: statusResponse.result?.details, - error: null, - ); - - default: - } - - await Future.delayed(const Duration(milliseconds: 500)); - } - - await trezorRepo.cancelWithdraw(taskId); - return BlocResponse( - result: null, - error: TextError(error: LocaleKeys.timeout.tr()), - ); - } -} diff --git a/lib/main.dart b/lib/main.dart index c86ef47194..fa94ab783c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -22,12 +22,9 @@ import 'package:web_dex/bloc/cex_market_data/cex_market_data.dart'; import 'package:web_dex/bloc/cex_market_data/mockup/performance_mode.dart'; import 'package:web_dex/bloc/coins_bloc/coins_repo.dart'; import 'package:web_dex/bloc/settings/settings_repository.dart'; -import 'package:web_dex/bloc/trezor_bloc/trezor_repo.dart'; -import 'package:web_dex/blocs/trezor_coins_bloc.dart'; import 'package:web_dex/blocs/wallets_repository.dart'; import 'package:web_dex/mm2/mm2.dart'; import 'package:web_dex/mm2/mm2_api/mm2_api.dart'; -import 'package:web_dex/mm2/mm2_api/mm2_api_trezor.dart'; import 'package:web_dex/model/stored_settings.dart'; import 'package:web_dex/performance_analytics/performance_analytics.dart'; import 'package:web_dex/analytics/widgets/analytics_lifecycle_handler.dart'; @@ -64,17 +61,7 @@ Future main() async { final mm2Api = Mm2Api(mm2: mm2, sdk: komodoDefiSdk); await AppBootstrapper.instance.ensureInitialized(komodoDefiSdk, mm2Api); - // Strange inter-dependencies here that should ideally not be the case. - final trezorRepo = TrezorRepo( - api: Mm2ApiTrezor(mm2.call), - kdfSdk: komodoDefiSdk, - ); - final trezor = TrezorCoinsBloc(trezorRepo: trezorRepo); - final coinsRepo = CoinsRepo( - kdfSdk: komodoDefiSdk, - mm2: mm2, - trezorBloc: trezor, - ); + final coinsRepo = CoinsRepo(kdfSdk: komodoDefiSdk, mm2: mm2); final walletsRepository = WalletsRepository( komodoDefiSdk, mm2Api, @@ -93,8 +80,6 @@ Future main() async { RepositoryProvider(create: (_) => komodoDefiSdk), RepositoryProvider(create: (_) => mm2Api), RepositoryProvider(create: (_) => coinsRepo), - RepositoryProvider(create: (_) => trezorRepo), - RepositoryProvider(create: (_) => trezor), RepositoryProvider(create: (_) => walletsRepository), ], child: const MyApp(), diff --git a/lib/mm2/mm2_api/mm2_api.dart b/lib/mm2/mm2_api/mm2_api.dart index ab5243edc5..8fcaa1ffa2 100644 --- a/lib/mm2/mm2_api/mm2_api.dart +++ b/lib/mm2/mm2_api/mm2_api.dart @@ -7,7 +7,6 @@ import 'package:rational/rational.dart'; import 'package:web_dex/bloc/coins_bloc/coins_repo.dart'; import 'package:web_dex/mm2/mm2.dart'; import 'package:web_dex/mm2/mm2_api/mm2_api_nft.dart'; -import 'package:web_dex/mm2/mm2_api/mm2_api_trezor.dart'; import 'package:web_dex/mm2/mm2_api/rpc/active_swaps/active_swaps_request.dart'; import 'package:web_dex/mm2/mm2_api/rpc/base.dart'; import 'package:web_dex/mm2/mm2_api/rpc/best_orders/best_orders_request.dart'; @@ -63,7 +62,6 @@ class Mm2Api { required KomodoDefiSdk sdk, }) : _sdk = sdk, _mm2 = mm2 { - trezor = Mm2ApiTrezor(_mm2.call); nft = Mm2ApiNft(_mm2.call, sdk); } @@ -74,7 +72,6 @@ class Mm2Api { // changes, we will tie into the SDK here. final KomodoDefiSdk _sdk; - late Mm2ApiTrezor trezor; late Mm2ApiNft nft; VersionResponse? _versionResponse; diff --git a/lib/mm2/mm2_api/mm2_api_trezor.dart b/lib/mm2/mm2_api/mm2_api_trezor.dart deleted file mode 100644 index 13986baf4f..0000000000 --- a/lib/mm2/mm2_api/mm2_api_trezor.dart +++ /dev/null @@ -1,188 +0,0 @@ -import 'package:komodo_defi_types/komodo_defi_type_utils.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/balance/trezor_balance_init/trezor_balance_init_request.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/balance/trezor_balance_init/trezor_balance_init_response.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/balance/trezor_balance_status/trezor_balance_status_request.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/balance/trezor_balance_status/trezor_balance_status_response.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/enable_utxo/trezor_enable_utxo/trezor_enable_utxo_request.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/enable_utxo/trezor_enable_utxo/trezor_enable_utxo_response.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/enable_utxo/trezor_enable_utxo_status/trezor_enable_utxo_status_request.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/enable_utxo/trezor_enable_utxo_status/trezor_enable_utxo_status_response.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/get_new_address/get_new_address_request.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/get_new_address/get_new_address_response.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/init/init_trezor/init_trezor_request.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/init/init_trezor/init_trezor_response.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/init/init_trezor_cancel/init_trezor_cancel_request.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/init/init_trezor_status/init_trezor_status_request.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/init/init_trezor_status/init_trezor_status_response.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/trezor_connection_status/trezor_connection_status_request.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/trezor_passphrase/trezor_passphrase_request.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/trezor_pin/trezor_pin_request.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/withdraw/trezor_withdraw/trezor_withdraw_request.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/withdraw/trezor_withdraw/trezor_withdraw_response.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/withdraw/trezor_withdraw_cancel/trezor_withdraw_cancel_request.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/withdraw/trezor_withdraw_status/trezor_withdraw_status_request.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/withdraw/trezor_withdraw_status/trezor_withdraw_status_response.dart'; -import 'package:web_dex/model/hw_wallet/trezor_connection_status.dart'; -import 'package:web_dex/shared/utils/utils.dart'; - -class Mm2ApiTrezor { - Mm2ApiTrezor(this.call); - - final Future Function(dynamic) call; - - Future init(InitTrezorReq request) async { - try { - return InitTrezorRes.fromJson(await call(request)); - } catch (e) { - return InitTrezorRes( - error: e.toString(), - ); - } - } - - Future initStatus(InitTrezorStatusReq request) async { - try { - return InitTrezorStatusRes.fromJson(await call(request)); - } catch (e) { - return InitTrezorStatusRes(error: e.toString()); - } - } - - Future initCancel(InitTrezorCancelReq request) async { - try { - await call(request); - } catch (e) { - log(e.toString(), path: 'api => initTrezorCancel', isError: true) - .ignore(); - } - } - - Future pin(TrezorPinRequest request) async { - try { - await call(request); - } catch (e) { - log(e.toString(), path: 'api => trezorPin', isError: true).ignore(); - } - } - - Future passphrase(TrezorPassphraseRequest request) async { - try { - await call(request); - } catch (e) { - log(e.toString(), path: 'api => trezorPassphrase', isError: true) - .ignore(); - } - } - - Future enableUtxo( - TrezorEnableUtxoReq request, - ) async { - try { - return TrezorEnableUtxoResponse.fromJson(await call(request)); - } catch (e) { - return TrezorEnableUtxoResponse(error: e.toString()); - } - } - - Future enableUtxoStatus( - TrezorEnableUtxoStatusReq request, - ) async { - try { - return TrezorEnableUtxoStatusResponse.fromJson(await call(request)); - } catch (e) { - return TrezorEnableUtxoStatusResponse(error: e.toString()); - } - } - - Future balanceInit( - TrezorBalanceInitRequest request, - ) async { - try { - return TrezorBalanceInitResponse.fromJson(await call(request)); - } catch (e) { - return TrezorBalanceInitResponse(error: e.toString()); - } - } - - Future balanceStatus( - TrezorBalanceStatusRequest request, - ) async { - try { - return TrezorBalanceStatusResponse.fromJson(await call(request)); - } catch (e) { - return TrezorBalanceStatusResponse(error: e.toString()); - } - } - - Future initNewAddress(String coin) async { - try { - final JsonMap response = - await call(TrezorGetNewAddressInitReq(coin: coin)); - return TrezorGetNewAddressInitResponse.fromJson(response); - } catch (e) { - return TrezorGetNewAddressInitResponse(error: e.toString()); - } - } - - Future getNewAddressStatus(int taskId) async { - try { - final JsonMap response = - await call(TrezorGetNewAddressStatusReq(taskId: taskId)); - return GetNewAddressResponse.fromJson(response); - } catch (e) { - return GetNewAddressResponse(error: e.toString()); - } - } - - Future cancelGetNewAddress(int taskId) async { - try { - await call(TrezorGetNewAddressCancelReq(taskId: taskId)); - } catch (e) { - log(e.toString(), path: 'api_trezor => getNewAddressCancel').ignore(); - } - } - - Future withdraw(TrezorWithdrawRequest request) async { - try { - return TrezorWithdrawResponse.fromJson(await call(request)); - } catch (e) { - return TrezorWithdrawResponse(error: e.toString()); - } - } - - Future withdrawStatus( - TrezorWithdrawStatusRequest request, - ) async { - try { - return TrezorWithdrawStatusResponse.fromJson(await call(request)); - } catch (e) { - return TrezorWithdrawStatusResponse(error: e.toString()); - } - } - - Future withdrawCancel(TrezorWithdrawCancelRequest request) async { - try { - await call(request); - } catch (e) { - log(e.toString(), path: 'api => withdrawCancel', isError: true).ignore(); - } - } - - Future getConnectionStatus(String pubKey) async { - try { - final JsonMap responseJson = - await call(TrezorConnectionStatusRequest(pubKey: pubKey)); - final String? status = responseJson['result']?['status'] as String?; - if (status == null) return TrezorConnectionStatus.unknown; - return TrezorConnectionStatus.fromString(status); - } catch (e, s) { - log( - 'Error getting trezor status: $e', - path: 'api => trezorConnectionStatus', - trace: s, - isError: true, - ).ignore(); - return TrezorConnectionStatus.unknown; - } - } -} diff --git a/lib/mm2/mm2_api/rpc/trezor/balance/trezor_balance_init/trezor_balance_init_request.dart b/lib/mm2/mm2_api/rpc/trezor/balance/trezor_balance_init/trezor_balance_init_request.dart deleted file mode 100644 index eaedcec1e3..0000000000 --- a/lib/mm2/mm2_api/rpc/trezor/balance/trezor_balance_init/trezor_balance_init_request.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:web_dex/model/coin.dart'; - -class TrezorBalanceInitRequest { - TrezorBalanceInitRequest({ - required this.coin, - }); - - static const String method = 'task::account_balance::init'; - late String userpass; - final Coin coin; - - Map toJson() { - return { - 'method': method, - 'userpass': userpass, - 'mmrpc': '2.0', - 'params': {'coin': coin.abbr, 'account_index': 0} - }; - } -} diff --git a/lib/mm2/mm2_api/rpc/trezor/balance/trezor_balance_init/trezor_balance_init_response.dart b/lib/mm2/mm2_api/rpc/trezor/balance/trezor_balance_init/trezor_balance_init_response.dart deleted file mode 100644 index 72501d56b6..0000000000 --- a/lib/mm2/mm2_api/rpc/trezor/balance/trezor_balance_init/trezor_balance_init_response.dart +++ /dev/null @@ -1,27 +0,0 @@ -class TrezorBalanceInitResponse { - TrezorBalanceInitResponse({this.result, this.error}); - - factory TrezorBalanceInitResponse.fromJson(Map json) { - return TrezorBalanceInitResponse( - result: TrezorBalanceInitResult.fromJson(json['result']), - error: json['error'], - ); - } - - final TrezorBalanceInitResult? result; - final dynamic error; -} - -class TrezorBalanceInitResult { - TrezorBalanceInitResult({required this.taskId}); - - static TrezorBalanceInitResult? fromJson(Map? json) { - if (json == null) return null; - - return TrezorBalanceInitResult( - taskId: json['task_id'], - ); - } - - final int taskId; -} diff --git a/lib/mm2/mm2_api/rpc/trezor/balance/trezor_balance_status/trezor_balance_status_request.dart b/lib/mm2/mm2_api/rpc/trezor/balance/trezor_balance_status/trezor_balance_status_request.dart deleted file mode 100644 index 1baced120d..0000000000 --- a/lib/mm2/mm2_api/rpc/trezor/balance/trezor_balance_status/trezor_balance_status_request.dart +++ /dev/null @@ -1,18 +0,0 @@ -class TrezorBalanceStatusRequest { - TrezorBalanceStatusRequest({required this.taskId}); - - static const String method = 'task::account_balance::status'; - late String userpass; - final int taskId; - - Map toJson() { - return { - 'userpass': userpass, - 'mmrpc': '2.0', - 'method': method, - 'params': { - 'task_id': taskId, - } - }; - } -} diff --git a/lib/mm2/mm2_api/rpc/trezor/balance/trezor_balance_status/trezor_balance_status_response.dart b/lib/mm2/mm2_api/rpc/trezor/balance/trezor_balance_status/trezor_balance_status_response.dart deleted file mode 100644 index 96186d99a1..0000000000 --- a/lib/mm2/mm2_api/rpc/trezor/balance/trezor_balance_status/trezor_balance_status_response.dart +++ /dev/null @@ -1,70 +0,0 @@ -import 'package:web_dex/model/hd_account/hd_account.dart'; -import 'package:web_dex/model/hw_wallet/trezor_status.dart'; - -class TrezorBalanceStatusResponse { - TrezorBalanceStatusResponse({this.result, this.error}); - - static TrezorBalanceStatusResponse fromJson(Map json) { - return TrezorBalanceStatusResponse( - result: TrezorBalanceStatusResult.fromJson(json['result'])); - } - - final TrezorBalanceStatusResult? result; - final dynamic error; -} - -class TrezorBalanceStatusResult { - TrezorBalanceStatusResult({ - required this.status, - required this.balanceDetails, - }); - - static TrezorBalanceStatusResult? fromJson(Map? json) { - if (json == null) return null; - - final status = InitTrezorStatus.fromJson(json['status']); - return TrezorBalanceStatusResult( - status: status, - balanceDetails: status == InitTrezorStatus.ok - ? TrezorBalanceDetails.fromJson(json['details']) - : null, - ); - } - - final InitTrezorStatus status; - final TrezorBalanceDetails? balanceDetails; -} - -class TrezorBalanceDetails { - TrezorBalanceDetails({ - required this.totalBalance, - required this.accounts, - }); - - static TrezorBalanceDetails? fromJson(Map? json) { - if (json == null) return null; - - return TrezorBalanceDetails( - totalBalance: HdBalance.fromJson(json['total_balance']), - // Current api version (89c2b7050) only supports single (index == 0) - // HD account for every asset. - // But since trezor enable_utxo rpc returns list of accounts - // (with a single element in it), and also there is a possibility of - // adding multiple accounts support, we'll store list of accounts in our - // model, although trezor balance rpc returns data for first account only. - accounts: [ - HdAccount( - accountIndex: json['account_index'], - // Since we only support single account, its balance is the same - // as total asset balance - totalBalance: HdBalance.fromJson(json['total_balance']), - addresses: json['addresses'] - .map((dynamic item) => HdAddress.fromJson(item)) - .toList(), - ) - ]); - } - - final HdBalance? totalBalance; - final List accounts; -} diff --git a/lib/mm2/mm2_api/rpc/trezor/enable_utxo/trezor_enable_utxo/trezor_enable_utxo_request.dart b/lib/mm2/mm2_api/rpc/trezor/enable_utxo/trezor_enable_utxo/trezor_enable_utxo_request.dart deleted file mode 100644 index 0c5f56c268..0000000000 --- a/lib/mm2/mm2_api/rpc/trezor/enable_utxo/trezor_enable_utxo/trezor_enable_utxo_request.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:komodo_defi_types/komodo_defi_types.dart'; - -class TrezorEnableUtxoReq { - TrezorEnableUtxoReq({required this.coin}); - - static const String method = 'task::enable_utxo::init'; - late String userpass; - final Asset coin; - - Map toJson() { - return { - 'method': method, - 'userpass': userpass, - 'mmrpc': '2.0', - 'params': { - 'ticker': coin.id.id, - 'activation_params': { - 'tx_history': true, - 'mode': { - 'rpc': 'Electrum', - 'rpc_data': { - 'servers': coin.protocol.requiredServers.electrum! - .map((server) => server.toJsonRequest()) - .toList(), - }, - }, - 'scan_policy': 'scan_if_new_wallet', - 'priv_key_policy': 'Trezor' - } - } - }; - } -} diff --git a/lib/mm2/mm2_api/rpc/trezor/enable_utxo/trezor_enable_utxo/trezor_enable_utxo_response.dart b/lib/mm2/mm2_api/rpc/trezor/enable_utxo/trezor_enable_utxo/trezor_enable_utxo_response.dart deleted file mode 100644 index 8979c2895c..0000000000 --- a/lib/mm2/mm2_api/rpc/trezor/enable_utxo/trezor_enable_utxo/trezor_enable_utxo_response.dart +++ /dev/null @@ -1,27 +0,0 @@ -class TrezorEnableUtxoResponse { - TrezorEnableUtxoResponse({this.result, this.error}); - - factory TrezorEnableUtxoResponse.fromJson(Map json) { - return TrezorEnableUtxoResponse( - result: TrezorEnableUtxoResult.fromJson(json['result']), - error: json['error'], - ); - } - - final TrezorEnableUtxoResult? result; - final dynamic error; -} - -class TrezorEnableUtxoResult { - TrezorEnableUtxoResult({required this.taskId}); - - static TrezorEnableUtxoResult? fromJson(Map? json) { - if (json == null) return null; - - return TrezorEnableUtxoResult( - taskId: json['task_id'], - ); - } - - final int taskId; -} diff --git a/lib/mm2/mm2_api/rpc/trezor/enable_utxo/trezor_enable_utxo_status/trezor_enable_utxo_status_request.dart b/lib/mm2/mm2_api/rpc/trezor/enable_utxo/trezor_enable_utxo_status/trezor_enable_utxo_status_request.dart deleted file mode 100644 index 7da90056f0..0000000000 --- a/lib/mm2/mm2_api/rpc/trezor/enable_utxo/trezor_enable_utxo_status/trezor_enable_utxo_status_request.dart +++ /dev/null @@ -1,18 +0,0 @@ -class TrezorEnableUtxoStatusReq { - TrezorEnableUtxoStatusReq({required this.taskId}); - - static const String method = 'task::enable_utxo::status'; - late String userpass; - final int taskId; - - Map toJson() { - return { - 'userpass': userpass, - 'mmrpc': '2.0', - 'method': method, - 'params': { - 'task_id': taskId, - } - }; - } -} diff --git a/lib/mm2/mm2_api/rpc/trezor/enable_utxo/trezor_enable_utxo_status/trezor_enable_utxo_status_response.dart b/lib/mm2/mm2_api/rpc/trezor/enable_utxo/trezor_enable_utxo_status/trezor_enable_utxo_status_response.dart deleted file mode 100644 index 3e066bc00e..0000000000 --- a/lib/mm2/mm2_api/rpc/trezor/enable_utxo/trezor_enable_utxo_status/trezor_enable_utxo_status_response.dart +++ /dev/null @@ -1,59 +0,0 @@ -import 'package:web_dex/model/hd_account/hd_account.dart'; -import 'package:web_dex/model/hw_wallet/init_trezor.dart'; -import 'package:web_dex/model/hw_wallet/trezor_status.dart'; - -class TrezorEnableUtxoStatusResponse { - TrezorEnableUtxoStatusResponse({this.result, this.error}); - - static TrezorEnableUtxoStatusResponse fromJson(Map json) { - return TrezorEnableUtxoStatusResponse( - result: TrezorEnableUtxoStatusResult.fromJson(json['result'])); - } - - final TrezorEnableUtxoStatusResult? result; - final dynamic error; -} - -class TrezorEnableUtxoStatusResult { - TrezorEnableUtxoStatusResult({ - required this.status, - this.details, - this.actionDetails, - }); - - static TrezorEnableUtxoStatusResult? fromJson(Map? json) { - if (json == null) return null; - - final InitTrezorStatus status = InitTrezorStatus.fromJson(json['status']); - return TrezorEnableUtxoStatusResult( - status: status, - details: status == InitTrezorStatus.ok - ? TrezorEnableDetails.fromJson(json['details']) - : null, - actionDetails: status == InitTrezorStatus.userActionRequired - ? TrezorUserAction.fromJson(json['details']) - : null); - } - - final InitTrezorStatus status; - final TrezorEnableDetails? details; - final TrezorUserAction? actionDetails; -} - -class TrezorEnableDetails { - TrezorEnableDetails({ - required this.accounts, - }); - - static TrezorEnableDetails? fromJson(Map? json) { - final Map? jsonData = json?['wallet_balance']; - if (jsonData == null) return null; - - return TrezorEnableDetails( - accounts: jsonData['accounts'] - .map((dynamic item) => HdAccount.fromJson(item)) - .toList()); - } - - final List accounts; -} diff --git a/lib/mm2/mm2_api/rpc/trezor/get_new_address/get_new_address_request.dart b/lib/mm2/mm2_api/rpc/trezor/get_new_address/get_new_address_request.dart deleted file mode 100644 index 1455c10276..0000000000 --- a/lib/mm2/mm2_api/rpc/trezor/get_new_address/get_new_address_request.dart +++ /dev/null @@ -1,59 +0,0 @@ -class TrezorGetNewAddressInitReq { - TrezorGetNewAddressInitReq({required this.coin}); - - static const String method = 'task::get_new_address::init'; - late String userpass; - final String coin; - - Map toJson() { - return { - 'method': method, - 'userpass': userpass, - 'mmrpc': '2.0', - 'params': { - 'coin': coin, - 'account_id': 0, - 'chain': 'External', - 'gap_limit': 20, - } - }; - } -} - -class TrezorGetNewAddressStatusReq { - TrezorGetNewAddressStatusReq({required this.taskId}); - - static const String method = 'task::get_new_address::status'; - final int taskId; - late String userpass; - - Map toJson() { - return { - 'method': method, - 'userpass': userpass, - 'mmrpc': '2.0', - 'params': { - 'task_id': taskId, - } - }; - } -} - -class TrezorGetNewAddressCancelReq { - TrezorGetNewAddressCancelReq({required this.taskId}); - - static const String method = 'task::get_new_address::cancel'; - final int taskId; - late String userpass; - - Map toJson() { - return { - 'method': method, - 'userpass': userpass, - 'mmrpc': '2.0', - 'params': { - 'task_id': taskId, - } - }; - } -} diff --git a/lib/mm2/mm2_api/rpc/trezor/get_new_address/get_new_address_response.dart b/lib/mm2/mm2_api/rpc/trezor/get_new_address/get_new_address_response.dart deleted file mode 100644 index 08301540dc..0000000000 --- a/lib/mm2/mm2_api/rpc/trezor/get_new_address/get_new_address_response.dart +++ /dev/null @@ -1,134 +0,0 @@ -import 'package:web_dex/model/hd_account/hd_account.dart'; - -class TrezorGetNewAddressInitResponse { - TrezorGetNewAddressInitResponse({this.result, this.error}); - - factory TrezorGetNewAddressInitResponse.fromJson(Map json) { - return TrezorGetNewAddressInitResponse( - result: TrezorGetNewAddressInitResult.fromJson(json['result']), - error: json['error'], - ); - } - - final TrezorGetNewAddressInitResult? result; - final dynamic error; -} - -class TrezorGetNewAddressInitResult { - TrezorGetNewAddressInitResult({required this.taskId}); - - static TrezorGetNewAddressInitResult? fromJson(Map? json) { - if (json == null) return null; - - return TrezorGetNewAddressInitResult( - taskId: json['task_id'], - ); - } - - final int taskId; -} - -class GetNewAddressResponse { - GetNewAddressResponse({ - this.result, - this.error, - }); - - factory GetNewAddressResponse.fromJson(Map json) { - return GetNewAddressResponse( - result: GetNewAddressResult.fromJson(json['result']), - error: json['error'], - ); - } - - final String? error; - final GetNewAddressResult? result; -} - -class GetNewAddressResult { - GetNewAddressResult({required this.status, required this.details}); - - static GetNewAddressResult? fromJson(Map? json) { - if (json == null) return null; - final GetNewAddressStatus status = - GetNewAddressStatus.fromString(json['status']); - final GetNewAddressResultDetails? details = - _getDetails(status, json['details']); - return GetNewAddressResult( - status: status, - details: details, - ); - } - - final GetNewAddressStatus status; - final GetNewAddressResultDetails? details; -} - -GetNewAddressResultDetails? _getDetails( - GetNewAddressStatus status, - dynamic json, -) { - if (json == null) return null; - - switch (status) { - case GetNewAddressStatus.ok: - final Map? newAddressJson = json['new_address']; - if (newAddressJson == null) return null; - - return GetNewAddressResultOkDetails( - newAddress: HdAddress.fromJson(newAddressJson)); - case GetNewAddressStatus.inProgress: - if (json is! Map) return null; - final confirmAddressJson = json['ConfirmAddress']; - if (confirmAddressJson != null) { - return GetNewAddressResultConfirmAddressDetails( - expectedAddress: confirmAddressJson['expected_address']); - } - - final requestingAccountBalanceJson = json['RequestingAccountBalance']; - if (requestingAccountBalanceJson != null) { - return const GetNewAddressResultRequestingAccountBalanceDetails(); - } - return null; - case GetNewAddressStatus.unknown: - return null; - } -} - -enum GetNewAddressStatus { - ok, - inProgress, - unknown; - - factory GetNewAddressStatus.fromString(String status) { - switch (status) { - case 'Ok': - return GetNewAddressStatus.ok; - case 'InProgress': - return GetNewAddressStatus.inProgress; - } - return GetNewAddressStatus.unknown; - } -} - -abstract class GetNewAddressResultDetails { - const GetNewAddressResultDetails(); -} - -class GetNewAddressResultConfirmAddressDetails - extends GetNewAddressResultDetails { - const GetNewAddressResultConfirmAddressDetails( - {required this.expectedAddress}); - - final String expectedAddress; -} - -class GetNewAddressResultRequestingAccountBalanceDetails - extends GetNewAddressResultDetails { - const GetNewAddressResultRequestingAccountBalanceDetails(); -} - -class GetNewAddressResultOkDetails extends GetNewAddressResultDetails { - const GetNewAddressResultOkDetails({required this.newAddress}); - final HdAddress newAddress; -} diff --git a/lib/mm2/mm2_api/rpc/trezor/init/init_trezor/init_trezor_request.dart b/lib/mm2/mm2_api/rpc/trezor/init/init_trezor/init_trezor_request.dart deleted file mode 100644 index 27fd31431a..0000000000 --- a/lib/mm2/mm2_api/rpc/trezor/init/init_trezor/init_trezor_request.dart +++ /dev/null @@ -1,18 +0,0 @@ -class InitTrezorReq { - InitTrezorReq({this.devicePubkey}); - - static const String method = 'task::init_trezor::init'; - final String? devicePubkey; - late String userpass; - - Map toJson() { - return { - 'method': method, - 'userpass': userpass, - 'mmrpc': '2.0', - 'params': { - if (devicePubkey != null) 'device_pubkey': devicePubkey, - } - }; - } -} diff --git a/lib/mm2/mm2_api/rpc/trezor/init/init_trezor/init_trezor_response.dart b/lib/mm2/mm2_api/rpc/trezor/init/init_trezor/init_trezor_response.dart deleted file mode 100644 index 073b740a60..0000000000 --- a/lib/mm2/mm2_api/rpc/trezor/init/init_trezor/init_trezor_response.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:web_dex/model/hw_wallet/init_trezor.dart'; - -class InitTrezorRes { - InitTrezorRes({ - this.result, - this.error, - this.id, - }); - - factory InitTrezorRes.fromJson(Map json) { - return InitTrezorRes( - result: InitTrezorResult.fromJson(json['result']), - error: json['error'], - id: json['id']); - } - - final InitTrezorResult? result; - final String? error; - final String? id; -} diff --git a/lib/mm2/mm2_api/rpc/trezor/init/init_trezor_cancel/init_trezor_cancel_request.dart b/lib/mm2/mm2_api/rpc/trezor/init/init_trezor_cancel/init_trezor_cancel_request.dart deleted file mode 100644 index 473c1d4d52..0000000000 --- a/lib/mm2/mm2_api/rpc/trezor/init/init_trezor_cancel/init_trezor_cancel_request.dart +++ /dev/null @@ -1,18 +0,0 @@ -class InitTrezorCancelReq { - InitTrezorCancelReq({required this.taskId}); - - static const String method = 'task::init_trezor::cancel'; - final int taskId; - late String userpass; - - Map toJson() { - return { - 'method': method, - 'userpass': userpass, - 'mmrpc': '2.0', - 'params': { - 'task_id': taskId, - } - }; - } -} diff --git a/lib/mm2/mm2_api/rpc/trezor/init/init_trezor_status/init_trezor_status_request.dart b/lib/mm2/mm2_api/rpc/trezor/init/init_trezor_status/init_trezor_status_request.dart deleted file mode 100644 index f5d43a198f..0000000000 --- a/lib/mm2/mm2_api/rpc/trezor/init/init_trezor_status/init_trezor_status_request.dart +++ /dev/null @@ -1,18 +0,0 @@ -class InitTrezorStatusReq { - InitTrezorStatusReq({required this.taskId}); - - static const String method = 'task::init_trezor::status'; - final int taskId; - late String userpass; - - Map toJson() { - return { - 'method': method, - 'userpass': userpass, - 'mmrpc': '2.0', - 'params': { - 'task_id': taskId, - } - }; - } -} diff --git a/lib/mm2/mm2_api/rpc/trezor/init/init_trezor_status/init_trezor_status_response.dart b/lib/mm2/mm2_api/rpc/trezor/init/init_trezor_status/init_trezor_status_response.dart deleted file mode 100644 index a6010a1cb5..0000000000 --- a/lib/mm2/mm2_api/rpc/trezor/init/init_trezor_status/init_trezor_status_response.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:web_dex/model/hw_wallet/init_trezor.dart'; - -class InitTrezorStatusRes { - InitTrezorStatusRes({ - this.result, - this.error, - this.errorType, - this.id, - }); - - factory InitTrezorStatusRes.fromJson(Map json) { - return InitTrezorStatusRes( - result: InitTrezorStatusData.fromJson(json['result']), - error: json['error'], - errorType: json['error_type'], - id: json['id']); - } - - final InitTrezorStatusData? result; - final String? error; - final String? errorType; - final String? id; -} diff --git a/lib/mm2/mm2_api/rpc/trezor/trezor_connection_status/trezor_connection_status_request.dart b/lib/mm2/mm2_api/rpc/trezor/trezor_connection_status/trezor_connection_status_request.dart deleted file mode 100644 index f0f1ee789c..0000000000 --- a/lib/mm2/mm2_api/rpc/trezor/trezor_connection_status/trezor_connection_status_request.dart +++ /dev/null @@ -1,20 +0,0 @@ -class TrezorConnectionStatusRequest { - TrezorConnectionStatusRequest({ - required this.pubKey, - }); - - static const String method = 'trezor_connection_status'; - late String userpass; - final String pubKey; - - Map toJson() { - return { - 'method': method, - 'userpass': userpass, - 'mmrpc': '2.0', - 'params': { - 'device_pubkey': pubKey, - } - }; - } -} diff --git a/lib/mm2/mm2_api/rpc/trezor/trezor_passphrase/trezor_passphrase_request.dart b/lib/mm2/mm2_api/rpc/trezor/trezor_passphrase/trezor_passphrase_request.dart deleted file mode 100644 index c9c4677864..0000000000 --- a/lib/mm2/mm2_api/rpc/trezor/trezor_passphrase/trezor_passphrase_request.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:web_dex/model/hw_wallet/trezor_task.dart'; - -class TrezorPassphraseRequest { - TrezorPassphraseRequest({required this.passphrase, required this.task}); - - final String passphrase; - final TrezorTask task; - late String userpass; - - String get method => 'task::${task.type.name}::user_action'; - - Map toJson() { - return { - 'method': method, - 'userpass': userpass, - 'mmrpc': '2.0', - 'params': { - 'task_id': task.taskId, - 'user_action': { - 'action_type': 'TrezorPassphrase', - 'passphrase': passphrase, - } - }, - 'id': null - }; - } -} diff --git a/lib/mm2/mm2_api/rpc/trezor/trezor_pin/trezor_pin_request.dart b/lib/mm2/mm2_api/rpc/trezor/trezor_pin/trezor_pin_request.dart deleted file mode 100644 index ee976bd51f..0000000000 --- a/lib/mm2/mm2_api/rpc/trezor/trezor_pin/trezor_pin_request.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:web_dex/model/hw_wallet/trezor_task.dart'; - -class TrezorPinRequest { - TrezorPinRequest({required this.pin, required this.task}); - - final String pin; - final TrezorTask task; - late String userpass; - - String get method => 'task::${task.type.name}::user_action'; - - Map toJson() { - return { - 'method': method, - 'userpass': userpass, - 'mmrpc': '2.0', - 'params': { - 'task_id': task.taskId, - 'user_action': { - 'action_type': 'TrezorPin', - 'pin': pin, - } - }, - 'id': null - }; - } -} diff --git a/lib/mm2/mm2_api/rpc/trezor/withdraw/trezor_withdraw/trezor_withdraw_request.dart b/lib/mm2/mm2_api/rpc/trezor/withdraw/trezor_withdraw/trezor_withdraw_request.dart deleted file mode 100644 index da17977a17..0000000000 --- a/lib/mm2/mm2_api/rpc/trezor/withdraw/trezor_withdraw/trezor_withdraw_request.dart +++ /dev/null @@ -1,40 +0,0 @@ -import 'package:web_dex/mm2/mm2_api/rpc/withdraw/fee/fee_request.dart'; -import 'package:web_dex/model/coin.dart'; - -class TrezorWithdrawRequest { - TrezorWithdrawRequest({ - required this.coin, - required this.from, - required this.to, - required this.amount, - this.max = false, - this.fee, - }); - - static const String method = 'task::withdraw::init'; - late String userpass; - final Coin coin; - final String to; - final String from; - final double amount; - final bool max; - final FeeRequest? fee; - - Map toJson() { - return { - 'method': method, - 'userpass': userpass, - 'mmrpc': '2.0', - 'params': { - 'coin': coin.abbr, - 'from': { - 'derivation_path': coin.getDerivationPath(from), - }, - 'to': to, - 'amount': amount, - 'max': max, - if (fee != null) 'fee': fee!.toJson(), - } - }; - } -} diff --git a/lib/mm2/mm2_api/rpc/trezor/withdraw/trezor_withdraw/trezor_withdraw_response.dart b/lib/mm2/mm2_api/rpc/trezor/withdraw/trezor_withdraw/trezor_withdraw_response.dart deleted file mode 100644 index 97975fb058..0000000000 --- a/lib/mm2/mm2_api/rpc/trezor/withdraw/trezor_withdraw/trezor_withdraw_response.dart +++ /dev/null @@ -1,25 +0,0 @@ -class TrezorWithdrawResponse { - TrezorWithdrawResponse({this.result, this.error}); - - factory TrezorWithdrawResponse.fromJson(Map json) { - return TrezorWithdrawResponse( - result: TrezorWithdrawResult.fromJson(json['result']), - error: json['error'], - ); - } - - String? error; - TrezorWithdrawResult? result; -} - -class TrezorWithdrawResult { - TrezorWithdrawResult({required this.taskId}); - - static TrezorWithdrawResult? fromJson(Map? json) { - if (json == null) return null; - - return TrezorWithdrawResult(taskId: json['task_id']); - } - - final int taskId; -} diff --git a/lib/mm2/mm2_api/rpc/trezor/withdraw/trezor_withdraw_cancel/trezor_withdraw_cancel_request.dart b/lib/mm2/mm2_api/rpc/trezor/withdraw/trezor_withdraw_cancel/trezor_withdraw_cancel_request.dart deleted file mode 100644 index fe5849415d..0000000000 --- a/lib/mm2/mm2_api/rpc/trezor/withdraw/trezor_withdraw_cancel/trezor_withdraw_cancel_request.dart +++ /dev/null @@ -1,18 +0,0 @@ -class TrezorWithdrawCancelRequest { - TrezorWithdrawCancelRequest({required this.taskId}); - - static const String method = 'task::withdraw::cancel'; - final int taskId; - late String userpass; - - Map toJson() { - return { - 'method': method, - 'userpass': userpass, - 'mmrpc': '2.0', - 'params': { - 'task_id': taskId, - } - }; - } -} diff --git a/lib/mm2/mm2_api/rpc/trezor/withdraw/trezor_withdraw_status/trezor_withdraw_status_request.dart b/lib/mm2/mm2_api/rpc/trezor/withdraw/trezor_withdraw_status/trezor_withdraw_status_request.dart deleted file mode 100644 index 89497ede44..0000000000 --- a/lib/mm2/mm2_api/rpc/trezor/withdraw/trezor_withdraw_status/trezor_withdraw_status_request.dart +++ /dev/null @@ -1,18 +0,0 @@ -class TrezorWithdrawStatusRequest { - TrezorWithdrawStatusRequest({required this.taskId}); - - static const String method = 'task::withdraw::status'; - late String userpass; - final int taskId; - - Map toJson() { - return { - 'method': method, - 'userpass': userpass, - 'mmrpc': '2.0', - 'params': { - 'task_id': taskId, - } - }; - } -} diff --git a/lib/mm2/mm2_api/rpc/trezor/withdraw/trezor_withdraw_status/trezor_withdraw_status_response.dart b/lib/mm2/mm2_api/rpc/trezor/withdraw/trezor_withdraw_status/trezor_withdraw_status_response.dart deleted file mode 100644 index 66fc533a0f..0000000000 --- a/lib/mm2/mm2_api/rpc/trezor/withdraw/trezor_withdraw_status/trezor_withdraw_status_response.dart +++ /dev/null @@ -1,56 +0,0 @@ -import 'package:web_dex/model/hw_wallet/init_trezor.dart'; -import 'package:web_dex/model/hw_wallet/trezor_progress_status.dart'; -import 'package:web_dex/model/hw_wallet/trezor_status.dart'; -import 'package:web_dex/model/hw_wallet/trezor_status_error.dart'; -import 'package:web_dex/model/withdraw_details/withdraw_details.dart'; - -class TrezorWithdrawStatusResponse { - TrezorWithdrawStatusResponse({this.result, this.error}); - - factory TrezorWithdrawStatusResponse.fromJson(Map json) { - return TrezorWithdrawStatusResponse( - result: TrezorWithdrawStatusResult.fromJson(json['result']), - error: json['error']); - } - - final TrezorWithdrawStatusResult? result; - final String? error; -} - -class TrezorWithdrawStatusResult { - TrezorWithdrawStatusResult({ - required this.status, - this.details, - this.progressDetails, - this.actionDetails, - this.errorDetails, - }); - - final InitTrezorStatus status; - final WithdrawDetails? details; - final TrezorProgressStatus? progressDetails; - final TrezorUserAction? actionDetails; - final TrezorStatusError? errorDetails; - - static TrezorWithdrawStatusResult? fromJson(Map? json) { - if (json == null) return null; - - final InitTrezorStatus status = InitTrezorStatus.fromJson(json['status']); - - return TrezorWithdrawStatusResult( - status: status, - details: status == InitTrezorStatus.ok - ? WithdrawDetails.fromTrezorJson(json['details']) - : null, - progressDetails: status == InitTrezorStatus.inProgress - ? TrezorProgressStatus.fromJson(json['details']) - : null, - actionDetails: status == InitTrezorStatus.userActionRequired - ? TrezorUserAction.fromJson(json['details']) - : null, - errorDetails: status == InitTrezorStatus.error - ? TrezorStatusError.fromJson(json['details']) - : null, - ); - } -} diff --git a/lib/model/coin.dart b/lib/model/coin.dart index ac9db89fba..ae99786795 100644 --- a/lib/model/coin.dart +++ b/lib/model/coin.dart @@ -126,14 +126,6 @@ class Coin { bool get hasFaucet => coinsWithFaucet.contains(abbr); - bool get hasTrezorSupport { - if (excludedAssetListTrezor.contains(abbr)) return false; - if (type == CoinType.utxo) return true; - if (type == CoinType.smartChain) return true; - - return false; - } - @Deprecated( 'TODO: Adapt SDK to cater for this use case and remove this method.') String? get _defaultTrezorAddress { diff --git a/lib/model/hw_wallet/init_trezor.dart b/lib/model/hw_wallet/init_trezor.dart deleted file mode 100644 index d1e222224a..0000000000 --- a/lib/model/hw_wallet/init_trezor.dart +++ /dev/null @@ -1,110 +0,0 @@ -import 'package:web_dex/model/hw_wallet/trezor_progress_status.dart'; -import 'package:web_dex/model/hw_wallet/trezor_status.dart'; -import 'package:web_dex/model/hw_wallet/trezor_status_error.dart'; - -class InitTrezorResult { - InitTrezorResult({required this.taskId}); - - static InitTrezorResult? fromJson(Map? json) { - if (json == null) return null; - return InitTrezorResult(taskId: json['task_id']); - } - - final int taskId; -} - -class InitTrezorStatusData { - InitTrezorStatusData({ - required this.trezorStatus, - required this.details, - }); - - static InitTrezorStatusData? fromJson(Map? json) { - if (json == null) return null; - - final status = InitTrezorStatus.fromJson(json['status']); - return InitTrezorStatusData( - trezorStatus: status, - details: TrezorStatusDetails.fromJson( - json['details'], - status, - )); - } - - final InitTrezorStatus trezorStatus; - final TrezorStatusDetails details; -} - -class TrezorStatusDetails { - TrezorStatusDetails({ - this.progressDetails, - this.details, - this.errorDetails, - this.actionDetails, - this.deviceDetails, - }); - - factory TrezorStatusDetails.fromJson(dynamic json, InitTrezorStatus status) { - switch (status) { - case InitTrezorStatus.inProgress: - return TrezorStatusDetails( - progressDetails: TrezorProgressStatus.fromJson(json), - ); - case InitTrezorStatus.userActionRequired: - return TrezorStatusDetails( - actionDetails: TrezorUserAction.fromJson(json), - ); - case InitTrezorStatus.ok: - return TrezorStatusDetails( - deviceDetails: TrezorDeviceDetails.fromJson(json)); - case InitTrezorStatus.error: - return TrezorStatusDetails( - errorDetails: TrezorStatusError.fromJson(json)); - default: - return TrezorStatusDetails(details: json); - } - } - - final dynamic details; - final TrezorProgressStatus? progressDetails; - final TrezorStatusError? errorDetails; - final TrezorUserAction? actionDetails; - final TrezorDeviceDetails? deviceDetails; -} - -class TrezorDeviceDetails { - TrezorDeviceDetails({ - required this.pubKey, - this.name, - this.deviceId, - }); - - static TrezorDeviceDetails fromJson(Map json) { - return TrezorDeviceDetails( - pubKey: json['device_pubkey'], - name: json['device_name'], - deviceId: json['device_id'], - ); - } - - final String pubKey; - final String? name; - final String? deviceId; -} - -enum TrezorUserAction { - enterTrezorPin, - enterTrezorPassphrase, - unknown; - - static TrezorUserAction fromJson(String json) { - switch (json) { - case 'EnterTrezorPin': - return TrezorUserAction.enterTrezorPin; - case 'EnterTrezorPassphrase': - return TrezorUserAction.enterTrezorPassphrase; - default: - return TrezorUserAction.unknown; - } - } -} diff --git a/lib/model/hw_wallet/trezor_progress_status.dart b/lib/model/hw_wallet/trezor_progress_status.dart deleted file mode 100644 index 58c0bdca37..0000000000 --- a/lib/model/hw_wallet/trezor_progress_status.dart +++ /dev/null @@ -1,25 +0,0 @@ -enum TrezorProgressStatus { - initializing, - waitingForTrezorToConnect, - waitingForUserToConfirmPubkey, - waitingForUserToConfirmSigning, - followHwDeviceInstructions, - unknown; - - static TrezorProgressStatus fromJson(String json) { - switch (json) { - case 'Initializing': - return TrezorProgressStatus.initializing; - case 'WaitingForTrezorToConnect': - return TrezorProgressStatus.waitingForTrezorToConnect; - case 'WaitingForUserToConfirmPubkey': - return TrezorProgressStatus.waitingForUserToConfirmPubkey; - case 'WaitingForUserToConfirmSigning': - return TrezorProgressStatus.waitingForUserToConfirmSigning; - case 'FollowHwDeviceInstructions': - return TrezorProgressStatus.followHwDeviceInstructions; - default: - return TrezorProgressStatus.unknown; - } - } -} diff --git a/lib/model/hw_wallet/trezor_status.dart b/lib/model/hw_wallet/trezor_status.dart deleted file mode 100644 index 800a43d267..0000000000 --- a/lib/model/hw_wallet/trezor_status.dart +++ /dev/null @@ -1,22 +0,0 @@ -enum InitTrezorStatus { - ok, - inProgress, - userActionRequired, - error, - unknown; - - static InitTrezorStatus fromJson(String json) { - switch (json) { - case 'Ok': - return InitTrezorStatus.ok; - case 'InProgress': - return InitTrezorStatus.inProgress; - case 'UserActionRequired': - return InitTrezorStatus.userActionRequired; - case 'Error': - return InitTrezorStatus.error; - default: - return InitTrezorStatus.unknown; - } - } -} diff --git a/lib/model/main_menu_value.dart b/lib/model/main_menu_value.dart index c5f664be75..19295c9834 100644 --- a/lib/model/main_menu_value.dart +++ b/lib/model/main_menu_value.dart @@ -1,5 +1,4 @@ import 'package:easy_localization/easy_localization.dart'; -import 'package:web_dex/bloc/trading_status/trading_status_bloc.dart'; import 'package:web_dex/generated/codegen_loader.g.dart'; enum MainMenuValue { diff --git a/lib/model/wallet.dart b/lib/model/wallet.dart index 6518bb71b5..2a18f8637f 100644 --- a/lib/model/wallet.dart +++ b/lib/model/wallet.dart @@ -166,7 +166,8 @@ extension KdfUserWalletExtension on KdfUser { config: WalletConfig( seedPhrase: '', pubKey: walletId.pubkeyHash, - activatedCoins: metadata.valueOrNull>('activated_coins') ?? [], + activatedCoins: + metadata.valueOrNull>('activated_coins') ?? [], hasBackup: metadata['has_backup'] as bool? ?? false, type: walletType, ), diff --git a/lib/router/navigators/page_content/page_content_router_delegate.dart b/lib/router/navigators/page_content/page_content_router_delegate.dart index 9e50ab1a35..d3d1db86eb 100644 --- a/lib/router/navigators/page_content/page_content_router_delegate.dart +++ b/lib/router/navigators/page_content/page_content_router_delegate.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:web_dex/app_config/app_config.dart'; import 'package:web_dex/model/main_menu_value.dart'; import 'package:web_dex/router/routes.dart'; import 'package:web_dex/router/state/routing_state.dart'; diff --git a/lib/router/parsers/root_route_parser.dart b/lib/router/parsers/root_route_parser.dart index ee12ad5d6a..3a791d5b63 100644 --- a/lib/router/parsers/root_route_parser.dart +++ b/lib/router/parsers/root_route_parser.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:web_dex/app_config/app_config.dart'; import 'package:web_dex/bloc/coins_bloc/coins_bloc.dart'; import 'package:web_dex/model/first_uri_segment.dart'; import 'package:web_dex/router/parsers/base_route_parser.dart'; diff --git a/lib/shared/utils/extensions/transaction_extensions.dart b/lib/shared/utils/extensions/transaction_extensions.dart index affd102eaf..1cc3c06dd6 100644 --- a/lib/shared/utils/extensions/transaction_extensions.dart +++ b/lib/shared/utils/extensions/transaction_extensions.dart @@ -13,8 +13,9 @@ extension TransactionExtensions on Transaction { Transaction sanitize(Set walletAddresses) { if (from.isEmpty) return this; final fromAddr = from.first; - final List sanitizedTo = List.from(to) - ..removeWhere((addr) => addr == fromAddr); + final List sanitizedTo = to.length > 1 + ? (List.from(to)..removeWhere((addr) => addr == fromAddr)) + : List.from(to); if (sanitizedTo.length > 1 && walletAddresses.isNotEmpty) { sanitizedTo.sort((a, b) { diff --git a/lib/views/common/hw_wallet_dialog/hw_dialog_init.dart b/lib/views/common/hw_wallet_dialog/hw_dialog_init.dart index c11b86e292..42b3117be4 100644 --- a/lib/views/common/hw_wallet_dialog/hw_dialog_init.dart +++ b/lib/views/common/hw_wallet_dialog/hw_dialog_init.dart @@ -1,7 +1,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:web_dex/bloc/trezor_init_bloc/trezor_init_bloc.dart'; +import 'package:web_dex/bloc/auth_bloc/auth_bloc.dart'; import 'package:web_dex/generated/codegen_loader.g.dart'; import 'package:web_dex/model/hw_wallet/hw_wallet.dart'; import 'package:komodo_ui_kit/komodo_ui_kit.dart'; @@ -19,8 +19,8 @@ class HwDialogInit extends StatelessWidget { HwDialogWalletSelect( onSelect: (WalletBrand brand) async { if (brand == WalletBrand.trezor && - !context.read().state.inProgress) { - context.read().add(const TrezorInit()); + !context.read().state.isLoading) { + context.read().add(const AuthTrezorInitAndAuthStarted()); } }, ), diff --git a/lib/views/common/hw_wallet_dialog/hw_dialog_wallet_select.dart b/lib/views/common/hw_wallet_dialog/hw_dialog_wallet_select.dart index 9fca825397..60375faa8a 100644 --- a/lib/views/common/hw_wallet_dialog/hw_dialog_wallet_select.dart +++ b/lib/views/common/hw_wallet_dialog/hw_dialog_wallet_select.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/svg.dart'; import 'package:web_dex/app_config/app_config.dart'; -import 'package:web_dex/bloc/trezor_init_bloc/trezor_init_bloc.dart'; +import 'package:web_dex/bloc/auth_bloc/auth_bloc.dart'; import 'package:web_dex/generated/codegen_loader.g.dart'; import 'package:web_dex/model/hw_wallet/hw_wallet.dart'; import 'package:web_dex/views/common/hw_wallet_dialog/constants.dart'; @@ -84,10 +84,8 @@ class _HwDialogWalletSelectState extends State { ], )), const SizedBox(height: 24), - BlocSelector( - selector: (state) { - return state.inProgress; - }, + BlocSelector( + selector: (state) => state.isLoading, builder: (context, inProgress) { return UiPrimaryButton( text: LocaleKeys.continueText.tr(), diff --git a/lib/views/common/hw_wallet_dialog/show_trezor_passphrase_dialog.dart b/lib/views/common/hw_wallet_dialog/show_trezor_passphrase_dialog.dart index fe3c08f2ad..50d3508173 100644 --- a/lib/views/common/hw_wallet_dialog/show_trezor_passphrase_dialog.dart +++ b/lib/views/common/hw_wallet_dialog/show_trezor_passphrase_dialog.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:web_dex/bloc/trezor_bloc/trezor_repo.dart'; +import 'package:web_dex/bloc/auth_bloc/auth_bloc.dart'; import 'package:web_dex/common/screen.dart'; import 'package:web_dex/dispatchers/popup_dispatcher.dart'; import 'package:web_dex/model/hw_wallet/trezor_task.dart'; @@ -25,9 +25,8 @@ Future showTrezorPassphraseDialog(TrezorTask task) async { onDismiss: close, popupContent: TrezorDialogSelectWallet( onComplete: (String passphrase) async { - final trezorRepo = RepositoryProvider.of(context); - await trezorRepo.sendPassphrase(passphrase, task); - // todo(yurii): handle invalid pin + final authBloc = context.read(); + authBloc.add(AuthTrezorPassphraseProvided(passphrase)); close(); }, ), diff --git a/lib/views/common/hw_wallet_dialog/show_trezor_pin_dialog.dart b/lib/views/common/hw_wallet_dialog/show_trezor_pin_dialog.dart index af6b3a7ea8..67396309f1 100644 --- a/lib/views/common/hw_wallet_dialog/show_trezor_pin_dialog.dart +++ b/lib/views/common/hw_wallet_dialog/show_trezor_pin_dialog.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:web_dex/bloc/trezor_bloc/trezor_repo.dart'; +import 'package:web_dex/bloc/auth_bloc/auth_bloc.dart'; import 'package:web_dex/common/screen.dart'; import 'package:web_dex/dispatchers/popup_dispatcher.dart'; import 'package:web_dex/model/hw_wallet/trezor_task.dart'; @@ -24,9 +24,8 @@ Future showTrezorPinDialog(TrezorTask task) async { onDismiss: close, popupContent: TrezorDialogPinPad( onComplete: (String pin) async { - final trezorRepo = RepositoryProvider.of(context); - await trezorRepo.sendPin(pin, task); - // todo(yurii): handle invalid pin + final authBloc = context.read(); + authBloc.add(AuthTrezorPinProvided(pin)); close(); }, onClose: close, diff --git a/lib/views/common/hw_wallet_dialog/trezor_steps/trezor_dialog_error.dart b/lib/views/common/hw_wallet_dialog/trezor_steps/trezor_dialog_error.dart index 56efd5bbd3..e9e225027c 100644 --- a/lib/views/common/hw_wallet_dialog/trezor_steps/trezor_dialog_error.dart +++ b/lib/views/common/hw_wallet_dialog/trezor_steps/trezor_dialog_error.dart @@ -2,18 +2,16 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:komodo_ui/komodo_ui.dart' show ErrorDisplay; import 'package:web_dex/app_config/app_config.dart'; -import 'package:web_dex/bloc/trezor_init_bloc/trezor_init_bloc.dart'; +import 'package:web_dex/bloc/auth_bloc/auth_bloc.dart'; import 'package:web_dex/generated/codegen_loader.g.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/base.dart'; -import 'package:web_dex/model/hw_wallet/trezor_status_error.dart'; import 'package:web_dex/shared/ui/ui_primary_button.dart'; -import 'package:web_dex/views/common/hw_wallet_dialog/constants.dart'; class TrezorDialogError extends StatelessWidget { const TrezorDialogError(this.error, {Key? key}) : super(key: key); - final dynamic error; + final String error; @override Widget build(BuildContext context) { @@ -23,38 +21,17 @@ class TrezorDialogError extends StatelessWidget { padding: const EdgeInsets.all(32), child: SvgPicture.asset('$assetsPath/ui_icons/error.svg'), ), - Text(_errorMessage, style: trezorDialogSubtitle), + ErrorDisplay( + message: LocaleKeys.trezorErrorBusy.tr(), + detailedMessage: error, + showIcon: false, + ), const SizedBox(height: 24), UiPrimaryButton( text: LocaleKeys.retryButtonText.tr(), - onPressed: () => - context.read().add(const TrezorInitReset()), + onPressed: () => context.read().add(AuthTrezorCancelled()), ), ], ); } - - String _parseErrorMessage(TrezorStatusError? error) { - if (error != null && error.error.contains('Error claiming an interface')) { - return LocaleKeys.trezorErrorBusy.tr(); - } - - switch (error?.errorData) { - case TrezorStatusErrorData.invalidPin: - return LocaleKeys.trezorErrorInvalidPin.tr(); - default: - return error?.error ?? LocaleKeys.somethingWrong.tr(); - } - } - - String get _errorMessage { - if (error is TrezorStatusError) { - return _parseErrorMessage(error); - } - if (error is BaseError) { - return error.message; - } - - return error.toString(); - } } diff --git a/lib/views/common/hw_wallet_dialog/trezor_steps/trezor_dialog_in_progress.dart b/lib/views/common/hw_wallet_dialog/trezor_steps/trezor_dialog_in_progress.dart index b28a8da864..10908354e6 100644 --- a/lib/views/common/hw_wallet_dialog/trezor_steps/trezor_dialog_in_progress.dart +++ b/lib/views/common/hw_wallet_dialog/trezor_steps/trezor_dialog_in_progress.dart @@ -1,7 +1,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:komodo_defi_sdk/komodo_defi_sdk.dart'; import 'package:web_dex/generated/codegen_loader.g.dart'; -import 'package:web_dex/model/hw_wallet/trezor_progress_status.dart'; import 'package:web_dex/shared/ui/ui_light_button.dart'; import 'package:web_dex/views/common/hw_wallet_dialog/constants.dart'; import 'package:komodo_ui_kit/komodo_ui_kit.dart'; @@ -11,7 +11,7 @@ class TrezorDialogInProgress extends StatelessWidget { {Key? key, required this.onClose}) : super(key: key); - final TrezorProgressStatus? progressStatus; + final AuthenticationStatus? progressStatus; final VoidCallback onClose; @override @@ -26,12 +26,12 @@ class TrezorDialogInProgress extends StatelessWidget { ), const SizedBox(height: 48), Builder(builder: (context) { - if (progressStatus == - TrezorProgressStatus.waitingForTrezorToConnect) { + if (progressStatus == AuthenticationStatus.waitingForDevice) { return _buildConnectTrezor(); } if (progressStatus == - TrezorProgressStatus.followHwDeviceInstructions) { + AuthenticationStatus.waitingForDeviceConfirmation || + progressStatus == AuthenticationStatus.passphraseRequired) { return _buildFollowInstructionsOnTrezor(); } diff --git a/lib/views/settings/widgets/security_settings/seed_settings/seed_confirmation/seed_confirmation.dart b/lib/views/settings/widgets/security_settings/seed_settings/seed_confirmation/seed_confirmation.dart index 3e39adb139..0eda5a39b4 100644 --- a/lib/views/settings/widgets/security_settings/seed_settings/seed_confirmation/seed_confirmation.dart +++ b/lib/views/settings/widgets/security_settings/seed_settings/seed_confirmation/seed_confirmation.dart @@ -6,7 +6,6 @@ import 'package:web_dex/bloc/security_settings/security_settings_bloc.dart'; import 'package:web_dex/bloc/security_settings/security_settings_event.dart'; import 'package:web_dex/common/screen.dart'; import 'package:web_dex/bloc/analytics/analytics_bloc.dart'; -import 'package:web_dex/bloc/analytics/analytics_event.dart'; import 'package:web_dex/analytics/events/security_events.dart'; import 'package:web_dex/generated/codegen_loader.g.dart'; import 'package:web_dex/model/text_error.dart'; diff --git a/lib/views/settings/widgets/security_settings/seed_settings/seed_show.dart b/lib/views/settings/widgets/security_settings/seed_settings/seed_show.dart index 45bdc313a6..2eea29d1c0 100644 --- a/lib/views/settings/widgets/security_settings/seed_settings/seed_show.dart +++ b/lib/views/settings/widgets/security_settings/seed_settings/seed_show.dart @@ -9,7 +9,6 @@ import 'package:web_dex/bloc/security_settings/security_settings_event.dart'; import 'package:web_dex/bloc/security_settings/security_settings_state.dart'; import 'package:web_dex/common/screen.dart'; import 'package:web_dex/bloc/analytics/analytics_bloc.dart'; -import 'package:web_dex/bloc/analytics/analytics_event.dart'; import 'package:web_dex/analytics/events/security_events.dart'; import 'package:web_dex/bloc/auth_bloc/auth_bloc.dart'; import 'package:web_dex/generated/codegen_loader.g.dart'; diff --git a/lib/views/wallet/coin_details/coin_details_info/charts/animated_portfolio_charts.dart b/lib/views/wallet/coin_details/coin_details_info/charts/animated_portfolio_charts.dart index 5bdea2a702..ee1ba34f64 100644 --- a/lib/views/wallet/coin_details/coin_details_info/charts/animated_portfolio_charts.dart +++ b/lib/views/wallet/coin_details/coin_details_info/charts/animated_portfolio_charts.dart @@ -4,7 +4,6 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:web_dex/bloc/cex_market_data/portfolio_growth/portfolio_growth_bloc.dart'; import 'package:web_dex/bloc/cex_market_data/profit_loss/profit_loss_bloc.dart'; import 'package:web_dex/bloc/analytics/analytics_bloc.dart'; -import 'package:web_dex/bloc/analytics/analytics_event.dart'; import 'package:web_dex/analytics/events/portfolio_events.dart'; import 'package:web_dex/generated/codegen_loader.g.dart'; import 'package:web_dex/model/coin.dart'; diff --git a/lib/views/wallet/coin_details/coin_details_info/coin_addresses.dart b/lib/views/wallet/coin_details/coin_details_info/coin_addresses.dart index d1f53c6b0e..cc182c869d 100644 --- a/lib/views/wallet/coin_details/coin_details_info/coin_addresses.dart +++ b/lib/views/wallet/coin_details/coin_details_info/coin_addresses.dart @@ -18,6 +18,7 @@ import 'package:web_dex/shared/widgets/coin_type_tag.dart'; import 'package:web_dex/shared/widgets/truncate_middle_text.dart'; import 'package:web_dex/views/wallet/coin_details/coin_page_type.dart'; import 'package:web_dex/views/wallet/coin_details/faucet/faucet_button.dart'; +import 'package:web_dex/views/wallet/coin_details/receive/trezor_new_address_confirmation.dart'; import 'package:web_dex/views/wallet/common/address_copy_button.dart'; import 'package:web_dex/views/wallet/common/address_icon.dart'; import 'package:web_dex/views/wallet/common/address_text.dart'; @@ -57,7 +58,23 @@ class _CoinAddressesState extends State { Widget build(BuildContext context) { return BlocBuilder( builder: (context, state) { - return BlocBuilder( + return BlocConsumer( + listenWhen: (prev, curr) => + prev.createAddressStatus != curr.createAddressStatus || + prev.newAddressState?.status != curr.newAddressState?.status, + listener: (context, blocState) { + if (blocState.newAddressState?.status == + NewAddressStatus.confirmAddress) { + final coinAddressesBloc = context.read(); + showDialog( + context: context, + builder: (context) => BlocProvider.value( + value: coinAddressesBloc, + child: const _NewAddressDialog(), + ), + ); + } + }, builder: (context, state) { return SliverToBoxAdapter( child: Column( @@ -234,7 +251,9 @@ class AddressCard extends StatelessWidget { SwapAddressTag(address: address), const Spacer(), AddressCopyButton( - address: address.address, coinAbbr: coin.abbr), + address: address.address, + coinAbbr: coin.abbr, + ), QrButton( coin: coin, address: address, @@ -253,7 +272,9 @@ class AddressCard extends StatelessWidget { AddressText(address: address.address), const SizedBox(width: 8), AddressCopyButton( - address: address.address, coinAbbr: coin.abbr), + address: address.address, + coinAbbr: coin.abbr, + ), QrButton(coin: coin, address: address), if (coin.hasFaucet) ConstrainedBox( @@ -316,7 +337,7 @@ class QrButton extends StatelessWidget { icon: const Icon(Icons.qr_code, size: 16), color: Theme.of(context).textTheme.bodyMedium!.color, onPressed: () { - showDialog( + showDialog( context: context, builder: (context) => AlertDialog( title: Row( @@ -370,7 +391,9 @@ class QrButton extends StatelessWidget { // Address row with copy and explorer link Container( padding: const EdgeInsets.symmetric( - horizontal: 8, vertical: 4), + horizontal: 8, + vertical: 4, + ), decoration: BoxDecoration( color: Theme.of(context).colorScheme.surfaceContainer, borderRadius: BorderRadius.circular(8), @@ -395,10 +418,11 @@ class QrButton extends StatelessWidget { .tr(args: [coin.abbr]), icon: const Icon(Icons.copy_rounded, size: 20), onPressed: () => copyToClipBoard( - context, - address.address, - LocaleKeys.copiedAddressToClipboard - .tr(args: [coin.abbr])), + context, + address.address, + LocaleKeys.copiedAddressToClipboard + .tr(args: [coin.abbr]), + ), ), ), // Explorer link button @@ -411,15 +435,18 @@ class QrButton extends StatelessWidget { icon: const Icon(Icons.open_in_new, size: 20), onPressed: () { final url = getAddressExplorerUrl( - coin, address.address); + coin, + address.address, + ); if (url.isNotEmpty) { launchURLString(url, inSeparateTab: true); } else { ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text(LocaleKeys - .explorerUnavailable - .tr())), + content: Text( + LocaleKeys.explorerUnavailable.tr(), + ), + ), ); } }, @@ -534,10 +561,11 @@ class PubkeyReceiveDialog extends StatelessWidget { .tr(args: [coin.abbr]), icon: const Icon(Icons.copy_rounded, size: 20), onPressed: () => copyToClipBoard( - context, - address.address, - LocaleKeys.copiedAddressToClipboard - .tr(args: [coin.abbr])), + context, + address.address, + LocaleKeys.copiedAddressToClipboard + .tr(args: [coin.abbr]), + ), ), ), // Explorer link button @@ -556,8 +584,9 @@ class PubkeyReceiveDialog extends StatelessWidget { } else { ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: - Text(LocaleKeys.explorerUnavailable.tr())), + content: + Text(LocaleKeys.explorerUnavailable.tr()), + ), ); } }, @@ -652,10 +681,10 @@ class HideZeroBalanceCheckbox extends StatelessWidget { class CreateButton extends StatelessWidget { const CreateButton({ - super.key, required this.status, required this.createAddressStatus, required this.cantCreateNewAddressReasons, + super.key, }); final FormStatus status; @@ -750,3 +779,50 @@ class QrCode extends StatelessWidget { ); } } + +class _NewAddressDialog extends StatelessWidget { + const _NewAddressDialog(); + + @override + Widget build(BuildContext context) { + return BlocConsumer( + listenWhen: (prev, curr) => + prev.newAddressState?.status != curr.newAddressState?.status, + listener: (context, state) { + final status = state.newAddressState?.status; + if (status == NewAddressStatus.completed || + status == NewAddressStatus.error || + status == NewAddressStatus.cancelled) { + Navigator.of(context).pop(); + } + }, + builder: (context, state) { + final newState = state.newAddressState; + final showAddress = newState?.status == NewAddressStatus.confirmAddress; + + return AlertDialog( + title: Text(LocaleKeys.creating.tr()), + content: SizedBox( + // slightly wider than the default to accommodate longer addresses + width: 450, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + if (showAddress) + TrezorNewAddressConfirmation( + address: newState?.expectedAddress ?? '', + ) + else + const SizedBox( + width: 32, + height: 32, + child: CircularProgressIndicator(), + ), + ], + ), + ), + ); + }, + ); + } +} diff --git a/lib/views/wallet/coin_details/receive/receive_address.dart b/lib/views/wallet/coin_details/receive/receive_address.dart index 9d45cea446..75f0c6e774 100644 --- a/lib/views/wallet/coin_details/receive/receive_address.dart +++ b/lib/views/wallet/coin_details/receive/receive_address.dart @@ -1,14 +1,10 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:komodo_defi_types/komodo_defi_types.dart'; -import 'package:web_dex/bloc/auth_bloc/auth_bloc.dart'; import 'package:web_dex/common/screen.dart'; import 'package:web_dex/generated/codegen_loader.g.dart'; -import 'package:web_dex/model/wallet.dart'; import 'package:web_dex/shared/widgets/copyable_address_dialog.dart'; import 'package:web_dex/views/wallet/coin_details/constants.dart'; -import 'package:web_dex/views/wallet/coin_details/receive/receive_address_trezor.dart'; class ReceiveAddress extends StatelessWidget { const ReceiveAddress({ @@ -28,16 +24,6 @@ class ReceiveAddress extends StatelessWidget { @override Widget build(BuildContext context) { - final currentWallet = context.watch().state.currentUser?.wallet; - if (currentWallet?.config.type == WalletType.trezor) { - return ReceiveAddressTrezor( - asset: asset, - selectedAddress: selectedAddress, - pubkeys: pubkeys, - onChanged: onChanged, - ); - } - if (selectedAddress == null) { return Text(LocaleKeys.addressNotFound.tr()); } diff --git a/lib/views/wallet/coin_details/receive/receive_address_trezor.dart b/lib/views/wallet/coin_details/receive/receive_address_trezor.dart deleted file mode 100644 index 43f4d52e66..0000000000 --- a/lib/views/wallet/coin_details/receive/receive_address_trezor.dart +++ /dev/null @@ -1,82 +0,0 @@ -import 'package:app_theme/app_theme.dart'; -import 'package:easy_localization/easy_localization.dart'; -import 'package:flutter/material.dart'; -import 'package:komodo_defi_types/komodo_defi_types.dart'; -import 'package:komodo_ui/komodo_ui.dart'; -import 'package:komodo_ui_kit/komodo_ui_kit.dart'; -import 'package:web_dex/generated/codegen_loader.g.dart'; -import 'package:web_dex/shared/utils/utils.dart'; -import 'package:web_dex/views/wallet/coin_details/receive/request_address_button.dart'; - -class ReceiveAddressTrezor extends StatelessWidget { - const ReceiveAddressTrezor( - {required this.asset, - required this.pubkeys, - required this.onChanged, - required this.selectedAddress, - super.key}); - - final Asset asset; - final AssetPubkeys pubkeys; - final void Function(PubkeyInfo?) onChanged; - final PubkeyInfo? selectedAddress; - - @override - Widget build(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (selectedAddress == null) - Text( - LocaleKeys.trezorNoAddresses.tr(), - style: theme.currentGlobal.textTheme.bodySmall, - ) - else - Row( - children: [ - Flexible( - child: SourceAddressField( - asset: asset, - pubkeys: pubkeys, - selectedAddress: selectedAddress, - onChanged: onChanged, - ), - ), - const SizedBox(width: 4), - _buildCopyButton(context) - ], - ), - _buildRequestButton(), - ], - ); - } - - Widget _buildRequestButton() { - return RequestAddressButton(asset, onSuccess: onChanged); - } - - Widget _buildCopyButton(BuildContext context) { - return Material( - type: MaterialType.transparency, - child: InkWell( - onTap: () { - copyToClipBoard(context, selectedAddress!.address); - }, - borderRadius: BorderRadius.circular(20), - child: UiTooltip( - message: LocaleKeys.copyAddressToClipboard - .tr(args: [asset.id.symbol.configSymbol]), - child: SizedBox( - width: 40, - height: 40, - child: Icon( - Icons.copy, - size: 16, - color: theme.currentGlobal.textTheme.bodySmall?.color, - ), - ), - ), - ), - ); - } -} diff --git a/lib/views/wallet/coin_details/receive/receive_details.dart b/lib/views/wallet/coin_details/receive/receive_details.dart deleted file mode 100644 index 7042548b59..0000000000 --- a/lib/views/wallet/coin_details/receive/receive_details.dart +++ /dev/null @@ -1,182 +0,0 @@ -import 'package:app_theme/app_theme.dart'; -import 'package:easy_localization/easy_localization.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:komodo_defi_types/komodo_defi_types.dart'; -import 'package:komodo_ui_kit/komodo_ui_kit.dart'; -import 'package:web_dex/bloc/auth_bloc/auth_bloc.dart'; -import 'package:web_dex/bloc/coins_bloc/asset_coin_extension.dart'; -import 'package:web_dex/common/screen.dart'; -import 'package:web_dex/generated/codegen_loader.g.dart'; -import 'package:web_dex/model/wallet.dart'; -import 'package:web_dex/shared/widgets/coin_type_tag.dart'; -import 'package:web_dex/shared/widgets/segwit_icon.dart'; -import 'package:web_dex/views/common/page_header/page_header.dart'; -import 'package:web_dex/views/common/pages/page_layout.dart'; -import 'package:web_dex/views/settings/widgets/security_settings/seed_settings/backup_seed_notification.dart'; -import 'package:web_dex/views/wallet/coin_details/constants.dart'; -import 'package:web_dex/views/wallet/coin_details/receive/qr_code_address.dart'; -import 'package:web_dex/views/wallet/coin_details/receive/receive_address.dart'; - -class ReceiveDetails extends StatelessWidget { - const ReceiveDetails({ - required this.asset, - required this.pubkeys, - required this.onBackButtonPressed, - super.key, - }); - - final Asset asset; - final AssetPubkeys pubkeys; - final VoidCallback onBackButtonPressed; - - @override - Widget build(BuildContext context) { - final scrollController = ScrollController(); - return BlocBuilder( - builder: (context, state) { - return PageLayout( - header: PageHeader( - title: LocaleKeys.receive.tr(), - widgetTitle: asset.id.isSegwit - ? const Padding( - padding: EdgeInsets.only(left: 6.0), - child: SegwitIcon(height: 22), - ) - : null, - backText: LocaleKeys.backToWallet.tr(), - onBackButtonPressed: onBackButtonPressed, - ), - content: Expanded( - child: DexScrollbar( - isMobile: isMobile, - scrollController: scrollController, - child: SingleChildScrollView( - controller: scrollController, - child: _ReceiveDetailsContent(asset: asset, pubkeys: pubkeys), - ), - ), - ), - ); - }, - ); - } -} - -class _ReceiveDetailsContent extends StatefulWidget { - const _ReceiveDetailsContent({required this.asset, required this.pubkeys}); - - final Asset asset; - final AssetPubkeys pubkeys; - - @override - State<_ReceiveDetailsContent> createState() => _ReceiveDetailsContentState(); -} - -class _ReceiveDetailsContentState extends State<_ReceiveDetailsContent> { - PubkeyInfo? _currentAddress; - - @override - void initState() { - super.initState(); - // Address initialization will be handled by ReceiveAddress widget - // which has access to the SDK's address management system - } - - @override - Widget build(BuildContext context) { - final ThemeData themeData = Theme.of(context); - - final currentWallet = context.read().state.currentUser?.wallet; - if (currentWallet?.config.hasBackup == false && - !widget.asset.protocol.isTestnet) { - return const BackupNotification(); - } - - return Container( - decoration: BoxDecoration( - color: isMobile ? themeData.cardColor : null, - borderRadius: BorderRadius.circular(18.0), - ), - padding: EdgeInsets.symmetric( - vertical: isMobile ? 25 : 0, - horizontal: 15, - ), - child: Column( - mainAxisSize: MainAxisSize.max, - children: [ - Text( - LocaleKeys.onlySendToThisAddress.tr(args: [widget.asset.id.name]), - style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 14), - ), - Container( - padding: const EdgeInsets.symmetric(vertical: 25, horizontal: 23), - margin: EdgeInsets.only(top: isMobile ? 25 : 15), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(18.0), - color: theme.mode == ThemeMode.dark - ? themeData.colorScheme.onSurface - : themeData.cardColor, - boxShadow: const [ - BoxShadow( - color: Color.fromRGBO(0, 0, 0, 0.08), - offset: Offset(0, 1), - blurRadius: 8, - ), - ], - ), - constraints: const BoxConstraints(maxWidth: receiveWidth), - child: Column( - children: [ - Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - LocaleKeys.network.tr(), - style: themeData.textTheme.bodyMedium?.copyWith( - fontWeight: FontWeight.w500, - fontSize: 14, - color: themeData.textTheme.labelLarge?.color, - ), - ), - CoinTypeTag(widget.asset.toCoin()), - ], - ), - const SizedBox(height: 30), - ReceiveAddress( - asset: widget.asset, - selectedAddress: _currentAddress, - pubkeys: widget.pubkeys, - onChanged: _onAddressChanged, - ), - const SizedBox(height: 30), - if (_currentAddress != null) - Column( - children: [ - QRCodeAddress(currentAddress: _currentAddress!.address), - const SizedBox(height: 15), - Text( - LocaleKeys.scanToGetAddress.tr(), - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - fontWeight: FontWeight.w400, - fontSize: 14, - color: themeData.textTheme.labelLarge?.color, - ), - ), - ], - ), - ], - ), - ), - ], - ), - ); - } - - void _onAddressChanged(PubkeyInfo? address) { - setState(() { - _currentAddress = address; - }); - } -} diff --git a/lib/views/wallet/coin_details/receive/request_address_button.dart b/lib/views/wallet/coin_details/receive/request_address_button.dart deleted file mode 100644 index ddfb387b20..0000000000 --- a/lib/views/wallet/coin_details/receive/request_address_button.dart +++ /dev/null @@ -1,168 +0,0 @@ -import 'dart:async'; - -import 'package:app_theme/app_theme.dart'; -import 'package:easy_localization/easy_localization.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:komodo_defi_types/komodo_defi_types.dart'; -import 'package:komodo_ui_kit/komodo_ui_kit.dart'; -import 'package:web_dex/bloc/coins_bloc/coins_repo.dart'; -import 'package:web_dex/dispatchers/popup_dispatcher.dart'; -import 'package:web_dex/generated/codegen_loader.g.dart'; -import 'package:web_dex/mm2/mm2_api/rpc/trezor/get_new_address/get_new_address_response.dart'; -import 'package:web_dex/router/state/routing_state.dart'; -import 'package:web_dex/shared/ui/ui_simple_border_button.dart'; -import 'package:web_dex/views/wallet/coin_details/receive/trezor_new_address_confirmation.dart'; - -class RequestAddressButton extends StatefulWidget { - const RequestAddressButton( - this.asset, { - required this.onSuccess, - super.key, - }); - - final Asset asset; - final void Function(PubkeyInfo) onSuccess; - - @override - State createState() => _RequestAddressButtonState(); -} - -class _RequestAddressButtonState extends State { - String? _message; - bool _inProgress = false; - PopupDispatcher? _confirmAddressDispatcher; - - @override - void dispose() { - final coinsRepository = RepositoryProvider.of(context); - _message = null; - _inProgress = false; - _confirmAddressDispatcher?.close(); - _confirmAddressDispatcher = null; - coinsRepository.trezor.unsubscribeFromNewAddressStatus(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildMessage(), - const SizedBox(height: 10), - _buildButton(), - ], - ); - } - - Widget _buildMessage() { - final message = _message; - if (message == null) return const SizedBox.shrink(); - - return Text( - message, - style: theme.currentGlobal.textTheme.bodySmall, - ); - } - - Widget _buildButton() { - return Row( - children: [ - UiSimpleBorderButton( - onPressed: _inProgress ? null : _getAddress, - child: SizedBox( - height: 24, - child: Row( - children: [ - if (_inProgress) - const UiSpinner(width: 10, height: 10, strokeWidth: 1) - else - const Icon(Icons.add, size: 16), - const SizedBox(width: 6), - Text(LocaleKeys.freshAddress.tr()), - ], - ), - ), - ), - ], - ); - } - - Future _getAddress() async { - final coinsRepository = RepositoryProvider.of(context); - setState(() { - _inProgress = true; - _message = null; - }); - - final taskId = await coinsRepository.trezor.initNewAddress(widget.asset); - if (taskId == null) return; - routingState.isBrowserNavigationBlocked = true; - coinsRepository.trezor - .subscribeOnNewAddressStatus(taskId, widget.asset, _onStatusUpdate); - } - - void _onStatusUpdate(GetNewAddressResponse initNewAddressStatus) { - final String? error = initNewAddressStatus.error; - if (error != null) { - _onError(error); - return; - } - - final GetNewAddressStatus? status = initNewAddressStatus.result?.status; - final GetNewAddressResultDetails? details = - initNewAddressStatus.result?.details; - - switch (status) { - case GetNewAddressStatus.inProgress: - if (details is GetNewAddressResultConfirmAddressDetails) { - _onConfirmAddressStatus(details); - } - return; - case GetNewAddressStatus.ok: - if (details is GetNewAddressResultOkDetails) { - _onOkStatus(details); - } - return; - case GetNewAddressStatus.unknown: - case null: - return; - } - } - - void _onConfirmAddressStatus( - GetNewAddressResultConfirmAddressDetails details) { - _confirmAddressDispatcher ??= PopupDispatcher( - width: 360, - barrierDismissible: false, - popupContent: - TrezorNewAddressConfirmation(address: details.expectedAddress), - ); - if (!_confirmAddressDispatcher!.isShown) { - _confirmAddressDispatcher?.show(); - } - } - - void _onOkStatus(GetNewAddressResultOkDetails details) { - final coinsRepository = RepositoryProvider.of(context); - coinsRepository.trezor.unsubscribeFromNewAddressStatus(); - _confirmAddressDispatcher?.close(); - _confirmAddressDispatcher = null; - routingState.isBrowserNavigationBlocked = false; - - widget.onSuccess(details.newAddress.toPubkeyInfo()); - setState(() { - _inProgress = false; - _message = null; - }); - } - - void _onError(String error) { - routingState.isBrowserNavigationBlocked = false; - setState(() { - _inProgress = false; - _message = error; - }); - } -} diff --git a/lib/views/wallet/coin_details/receive/trezor_new_address_confirmation.dart b/lib/views/wallet/coin_details/receive/trezor_new_address_confirmation.dart index c01d160433..f6701c24ba 100644 --- a/lib/views/wallet/coin_details/receive/trezor_new_address_confirmation.dart +++ b/lib/views/wallet/coin_details/receive/trezor_new_address_confirmation.dart @@ -1,25 +1,40 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:komodo_ui_kit/komodo_ui_kit.dart'; +import 'package:qr_flutter/qr_flutter.dart' + show QrErrorCorrectLevel, QrImageView; import 'package:web_dex/generated/codegen_loader.g.dart'; import 'package:web_dex/shared/utils/utils.dart'; -import 'package:web_dex/views/wallet/coin_details/receive/qr_code_address.dart'; -import 'package:komodo_ui_kit/komodo_ui_kit.dart'; class TrezorNewAddressConfirmation extends StatelessWidget { - const TrezorNewAddressConfirmation({super.key, required this.address}); + const TrezorNewAddressConfirmation({ + required this.address, + this.maxWidth = 300, + super.key, + }); + final String address; + final double maxWidth; @override Widget build(BuildContext context) { - final theme = Theme.of(context); + final themeData = Theme.of(context); return Column( children: [ Text( LocaleKeys.confirmOnTrezor.tr(), - style: theme.textTheme.titleLarge, + style: themeData.textTheme.titleLarge, ), const SizedBox(height: 24), - QRCodeAddress(currentAddress: address, size: 160), + ClipRRect( + borderRadius: BorderRadius.circular(8), + child: QrImageView( + data: address, + backgroundColor: Theme.of(context).textTheme.bodyMedium!.color!, + size: 200.0, + errorCorrectionLevel: QrErrorCorrectLevel.H, + ), + ), const SizedBox(height: 24), UiTextFormField( readOnly: true, @@ -28,7 +43,7 @@ class TrezorNewAddressConfirmation extends StatelessWidget { suffixIcon: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 20, maxHeight: 20), child: IconButton( - padding: const EdgeInsets.all(0.0), + padding: EdgeInsets.zero, hoverColor: Colors.transparent, highlightColor: Colors.transparent, onPressed: () => copyToClipBoard(context, address), diff --git a/lib/views/wallet/coin_details/transactions/transaction_list_item.dart b/lib/views/wallet/coin_details/transactions/transaction_list_item.dart index 34098c7adf..de51a9a2ef 100644 --- a/lib/views/wallet/coin_details/transactions/transaction_list_item.dart +++ b/lib/views/wallet/coin_details/transactions/transaction_list_item.dart @@ -283,8 +283,15 @@ class _TransactionAddress extends StatelessWidget { @override Widget build(BuildContext context) { - final myAddress = - transaction.isIncoming ? transaction.to.first : transaction.from.first; + String myAddress; + List addressList = + transaction.isIncoming ? transaction.to : transaction.from; + + if (addressList.isNotEmpty) { + myAddress = addressList.first; + } else { + myAddress = LocaleKeys.unknown.tr(); + } return Row( children: [ diff --git a/lib/views/wallet/coins_manager/coins_manager_filters_dropdown.dart b/lib/views/wallet/coins_manager/coins_manager_filters_dropdown.dart index 23907c4478..e88bdee030 100644 --- a/lib/views/wallet/coins_manager/coins_manager_filters_dropdown.dart +++ b/lib/views/wallet/coins_manager/coins_manager_filters_dropdown.dart @@ -149,8 +149,8 @@ class _Dropdown extends StatelessWidget { .firstWhereOrNull((coin) => coin.type == type) != null; case WalletType.trezor: - return coinsBloc.state.coins.values.firstWhereOrNull( - (coin) => coin.type == type && coin.hasTrezorSupport) != + return coinsBloc.state.coins.values + .firstWhereOrNull((coin) => coin.type == type) != null; case WalletType.metamask: case WalletType.keplr: diff --git a/lib/views/wallet/wallet_page/charts/coin_prices_chart.dart b/lib/views/wallet/wallet_page/charts/coin_prices_chart.dart index a8469e06d3..f01fc079d9 100644 --- a/lib/views/wallet/wallet_page/charts/coin_prices_chart.dart +++ b/lib/views/wallet/wallet_page/charts/coin_prices_chart.dart @@ -3,7 +3,6 @@ import 'package:flutter/material.dart'; import 'package:web_dex/generated/codegen_loader.g.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:komodo_defi_sdk/komodo_defi_sdk.dart'; import 'package:komodo_ui/komodo_ui.dart'; import 'package:komodo_ui_kit/komodo_ui_kit.dart'; import 'package:web_dex/bloc/cex_market_data/price_chart/models/price_chart_data.dart'; @@ -11,7 +10,6 @@ import 'package:web_dex/bloc/cex_market_data/price_chart/models/time_period.dart import 'package:web_dex/bloc/cex_market_data/price_chart/price_chart_bloc.dart'; import 'package:web_dex/bloc/cex_market_data/price_chart/price_chart_event.dart'; import 'package:web_dex/bloc/cex_market_data/price_chart/price_chart_state.dart'; -import 'package:web_dex/bloc/coins_bloc/asset_coin_extension.dart'; import 'package:web_dex/shared/utils/utils.dart'; import 'package:web_dex/shared/widgets/coin_select_item_widget.dart'; @@ -51,11 +49,7 @@ class PriceChartPage extends StatelessWidget { state.data.firstOrNull?.data.lastOrNull?.usdValue ?? 0, ), ), - availableCoins: state.availableCoins.keys - .map( - (e) => getSdkAsset(context.read(), e).id, - ) - .toList(), + availableCoins: state.availableCoins.keys.toList(), selectedCoinId: state.data.firstOrNull?.info.ticker, onCoinSelected: (coinId) { context.read().add( diff --git a/lib/views/wallet/wallet_page/common/asset_list_item_desktop.dart b/lib/views/wallet/wallet_page/common/asset_list_item_desktop.dart index b8dd47ee6a..fd55febbee 100644 --- a/lib/views/wallet/wallet_page/common/asset_list_item_desktop.dart +++ b/lib/views/wallet/wallet_page/common/asset_list_item_desktop.dart @@ -62,7 +62,7 @@ class AssetListItemDesktop extends StatelessWidget { percentage: priceChangePercentage24h ?? 0, showIcon: true, iconSize: 16, - valuePrecision: 2, + percentagePrecision: 2, ), ), ), 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 e73cfee6e0..1da4f57be7 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,7 +122,7 @@ class _GroupedAssetTickerItemState extends State { state.get24hChangeForAsset(_primaryAsset) ?? 0, showIcon: true, iconSize: 16, - valuePrecision: 2, + percentagePrecision: 2, ); }, ), diff --git a/lib/views/wallet/wallet_page/wallet_main/wallet_overview.dart b/lib/views/wallet/wallet_page/wallet_main/wallet_overview.dart index 2dca71c633..0905575178 100644 --- a/lib/views/wallet/wallet_page/wallet_main/wallet_overview.dart +++ b/lib/views/wallet/wallet_page/wallet_main/wallet_overview.dart @@ -95,7 +95,7 @@ class _WalletOverviewState extends State { label: TrendPercentageText( percentage: totalChange, suffix: Text(TimePeriod.oneDay.formatted()), - valuePrecision: 2, + percentagePrecision: 2, ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), diff --git a/lib/views/wallets_manager/widgets/hardware_wallets_manager.dart b/lib/views/wallets_manager/widgets/hardware_wallets_manager.dart index eca407fdff..b752c62e39 100644 --- a/lib/views/wallets_manager/widgets/hardware_wallets_manager.dart +++ b/lib/views/wallets_manager/widgets/hardware_wallets_manager.dart @@ -1,18 +1,13 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/widgets.dart'; 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'; import 'package:web_dex/bloc/analytics/analytics_bloc.dart'; -import 'package:web_dex/bloc/analytics/analytics_event.dart'; import 'package:web_dex/bloc/auth_bloc/auth_bloc.dart'; import 'package:web_dex/bloc/coins_bloc/coins_bloc.dart'; -import 'package:web_dex/bloc/trezor_init_bloc/trezor_init_bloc.dart'; import 'package:web_dex/generated/codegen_loader.g.dart'; -import 'package:web_dex/model/authorize_mode.dart'; -import 'package:web_dex/model/hw_wallet/init_trezor.dart'; -import 'package:web_dex/model/hw_wallet/trezor_status.dart'; import 'package:web_dex/model/main_menu_value.dart'; -import 'package:web_dex/model/text_error.dart'; import 'package:web_dex/router/state/routing_state.dart'; import 'package:web_dex/views/common/hw_wallet_dialog/hw_dialog_init.dart'; import 'package:web_dex/views/common/hw_wallet_dialog/trezor_steps/trezor_dialog_error.dart'; @@ -57,38 +52,33 @@ class _HardwareWalletsManagerViewState extends State { @override void initState() { - context.read().add(const TrezorInitReset()); + context.read().add(AuthTrezorCancelled()); super.initState(); } @override Widget build(BuildContext context) { - return BlocListener( + return BlocListener( listener: (context, state) { final status = state.status; - if (status?.trezorStatus == InitTrezorStatus.ok) { - _successfulTrezorLogin(context, state.kdfUser!); + if (status == AuthenticationStatus.completed) { + _successfulTrezorLogin(context, state.currentUser!); } }, - child: BlocSelector( - selector: (state) { - return state.error; - }, + child: BlocSelector( + selector: (state) => state.authError, builder: (context, error) { if (error != null) { - return TrezorDialogError(error); + return TrezorDialogError(error.message); } - return _buildContent(); + return _HardwareWalletManagerPopupContent(widget: widget); }, ), ); } void _successfulTrezorLogin(BuildContext context, KdfUser kdfUser) { - context.read().add( - AuthModeChanged(mode: AuthorizeMode.logIn, currentUser: kdfUser), - ); context.read().add(CoinsSessionStarted(kdfUser)); context.read().logEvent( walletsManagerEventsFactory.createEvent( @@ -98,59 +88,65 @@ class _HardwareWalletsManagerViewState routingState.selectedMenu = MainMenuValue.wallet; widget.close(); } +} - Widget _buildContent() { - return BlocSelector( - selector: (state) { - return state.status; - }, - builder: (context, initStatus) { - switch (initStatus?.trezorStatus) { +class _HardwareWalletManagerPopupContent extends StatelessWidget { + const _HardwareWalletManagerPopupContent({ + required this.widget, + }); + + final HardwareWalletsManagerView widget; + + @override + Widget build(BuildContext context) { + return BlocSelector( + selector: (state) => state.authenticationState, + builder: (context, state) { + final initStatus = state?.status; + switch (initStatus) { case null: return HwDialogInit(close: widget.close); - case InitTrezorStatus.inProgress: + case AuthenticationStatus.initializing: + case AuthenticationStatus.authenticating: return TrezorDialogInProgress( - initStatus?.details.progressDetails, + initStatus, onClose: widget.close, ); - case InitTrezorStatus.userActionRequired: - final TrezorUserAction? actionDetails = - initStatus?.details.actionDetails; - if (actionDetails == TrezorUserAction.enterTrezorPin) { - return TrezorDialogPinPad( - onComplete: (String pin) { - context.read().add(TrezorInitSendPin(pin)); - }, - onClose: () async { - context.read().add(const TrezorInitReset()); - }, - ); - } else if (actionDetails == - TrezorUserAction.enterTrezorPassphrase) { - return TrezorDialogSelectWallet( - onComplete: (String passphrase) { - context - .read() - .add(TrezorInitSendPassphrase(passphrase)); - }, - ); - } + case AuthenticationStatus.pinRequired: + return TrezorDialogPinPad( + onComplete: (String pin) { + context.read().add(AuthTrezorPinProvided(pin)); + }, + onClose: () async { + context.read().add(const AuthTrezorCancelled()); + }, + ); + case AuthenticationStatus.passphraseRequired: + return TrezorDialogSelectWallet( + onComplete: (String passphrase) { + context + .read() + .add(AuthTrezorPassphraseProvided(passphrase)); + }, + ); + case AuthenticationStatus.waitingForDevice: + case AuthenticationStatus.waitingForDeviceConfirmation: return TrezorDialogMessage( '${LocaleKeys.userActionRequired.tr()}:' - ' ${initStatus?.details.actionDetails?.name ?? LocaleKeys.unknown.tr().toLowerCase()}', + '${LocaleKeys.followTrezorInstructions.tr()}', ); - case InitTrezorStatus.error: - return TrezorDialogError(initStatus?.details.errorDetails); + case AuthenticationStatus.error: + return TrezorDialogError(state?.error ?? LocaleKeys.unknown.tr()); - case InitTrezorStatus.ok: + case AuthenticationStatus.completed: return TrezorDialogSuccess(onClose: widget.close); default: - return TrezorDialogMessage(initStatus!.trezorStatus.name); + return TrezorDialogMessage(initStatus.name); } }, ); diff --git a/lib/views/wallets_manager/widgets/iguana_wallets_manager.dart b/lib/views/wallets_manager/widgets/iguana_wallets_manager.dart index c52dae10b7..23b52f7f7a 100644 --- a/lib/views/wallets_manager/widgets/iguana_wallets_manager.dart +++ b/lib/views/wallets_manager/widgets/iguana_wallets_manager.dart @@ -4,7 +4,6 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:komodo_ui_kit/komodo_ui_kit.dart'; import 'package:web_dex/analytics/events/user_acquisition_events.dart'; import 'package:web_dex/bloc/analytics/analytics_bloc.dart'; -import 'package:web_dex/bloc/analytics/analytics_event.dart'; import 'package:web_dex/bloc/auth_bloc/auth_bloc.dart'; import 'package:web_dex/bloc/coins_bloc/coins_bloc.dart'; import 'package:web_dex/common/screen.dart'; diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index a7a6478b6d..691053fdaa 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -26,7 +26,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin")) FlutterSecureStorageDarwinPlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStorageDarwinPlugin")) - FLALocalAuthPlugin.register(with: registry.registrar(forPlugin: "FLALocalAuthPlugin")) + LocalAuthPlugin.register(with: registry.registrar(forPlugin: "LocalAuthPlugin")) MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin")) FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) diff --git a/packages/komodo_ui_kit/pubspec.lock b/packages/komodo_ui_kit/pubspec.lock index 6131f88827..23c9abe233 100644 --- a/packages/komodo_ui_kit/pubspec.lock +++ b/packages/komodo_ui_kit/pubspec.lock @@ -95,7 +95,7 @@ packages: description: path: "packages/komodo_defi_rpc_methods" ref: dev - resolved-ref: "9cf9e3756542edea823edf9fb81dbb8abb8186ca" + resolved-ref: aa51985a2148820cc7a784f8fc2a926ee8be1c26 url: "https://github.com/KomodoPlatform/komodo-defi-sdk-flutter.git" source: git version: "0.2.0+0" @@ -104,7 +104,7 @@ packages: description: path: "packages/komodo_defi_types" ref: dev - resolved-ref: "9cf9e3756542edea823edf9fb81dbb8abb8186ca" + resolved-ref: aa51985a2148820cc7a784f8fc2a926ee8be1c26 url: "https://github.com/KomodoPlatform/komodo-defi-sdk-flutter.git" source: git version: "0.2.0+0" @@ -113,7 +113,7 @@ packages: description: path: "packages/komodo_ui" ref: dev - resolved-ref: "9cf9e3756542edea823edf9fb81dbb8abb8186ca" + resolved-ref: aa51985a2148820cc7a784f8fc2a926ee8be1c26 url: "https://github.com/KomodoPlatform/komodo-defi-sdk-flutter.git" source: git version: "0.2.0+0" diff --git a/pubspec.lock b/pubspec.lock index 19e43862a8..a00af0e653 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -648,7 +648,7 @@ packages: description: path: "packages/komodo_cex_market_data" ref: dev - resolved-ref: "9cf9e3756542edea823edf9fb81dbb8abb8186ca" + resolved-ref: aa51985a2148820cc7a784f8fc2a926ee8be1c26 url: "https://github.com/KomodoPlatform/komodo-defi-sdk-flutter.git" source: git version: "0.0.1" @@ -657,7 +657,7 @@ packages: description: path: "packages/komodo_coin_updates" ref: dev - resolved-ref: "9cf9e3756542edea823edf9fb81dbb8abb8186ca" + resolved-ref: aa51985a2148820cc7a784f8fc2a926ee8be1c26 url: "https://github.com/KomodoPlatform/komodo-defi-sdk-flutter.git" source: git version: "1.0.0" @@ -666,7 +666,7 @@ packages: description: path: "packages/komodo_coins" ref: dev - resolved-ref: "9cf9e3756542edea823edf9fb81dbb8abb8186ca" + resolved-ref: aa51985a2148820cc7a784f8fc2a926ee8be1c26 url: "https://github.com/KomodoPlatform/komodo-defi-sdk-flutter.git" source: git version: "0.2.0+0" @@ -675,7 +675,7 @@ packages: description: path: "packages/komodo_defi_framework" ref: dev - resolved-ref: "9cf9e3756542edea823edf9fb81dbb8abb8186ca" + resolved-ref: aa51985a2148820cc7a784f8fc2a926ee8be1c26 url: "https://github.com/KomodoPlatform/komodo-defi-sdk-flutter.git" source: git version: "0.2.0" @@ -684,7 +684,7 @@ packages: description: path: "packages/komodo_defi_local_auth" ref: dev - resolved-ref: "9cf9e3756542edea823edf9fb81dbb8abb8186ca" + resolved-ref: aa51985a2148820cc7a784f8fc2a926ee8be1c26 url: "https://github.com/KomodoPlatform/komodo-defi-sdk-flutter.git" source: git version: "0.2.0+0" @@ -693,7 +693,7 @@ packages: description: path: "packages/komodo_defi_rpc_methods" ref: dev - resolved-ref: "9cf9e3756542edea823edf9fb81dbb8abb8186ca" + resolved-ref: aa51985a2148820cc7a784f8fc2a926ee8be1c26 url: "https://github.com/KomodoPlatform/komodo-defi-sdk-flutter.git" source: git version: "0.2.0+0" @@ -702,7 +702,7 @@ packages: description: path: "packages/komodo_defi_sdk" ref: dev - resolved-ref: "9cf9e3756542edea823edf9fb81dbb8abb8186ca" + resolved-ref: aa51985a2148820cc7a784f8fc2a926ee8be1c26 url: "https://github.com/KomodoPlatform/komodo-defi-sdk-flutter.git" source: git version: "0.2.0+0" @@ -711,7 +711,7 @@ packages: description: path: "packages/komodo_defi_types" ref: dev - resolved-ref: "9cf9e3756542edea823edf9fb81dbb8abb8186ca" + resolved-ref: aa51985a2148820cc7a784f8fc2a926ee8be1c26 url: "https://github.com/KomodoPlatform/komodo-defi-sdk-flutter.git" source: git version: "0.2.0+0" @@ -727,7 +727,7 @@ packages: description: path: "packages/komodo_ui" ref: dev - resolved-ref: "9cf9e3756542edea823edf9fb81dbb8abb8186ca" + resolved-ref: aa51985a2148820cc7a784f8fc2a926ee8be1c26 url: "https://github.com/KomodoPlatform/komodo-defi-sdk-flutter.git" source: git version: "0.2.0+0" @@ -743,7 +743,7 @@ packages: description: path: "packages/komodo_wallet_build_transformer" ref: dev - resolved-ref: "9cf9e3756542edea823edf9fb81dbb8abb8186ca" + resolved-ref: aa51985a2148820cc7a784f8fc2a926ee8be1c26 url: "https://github.com/KomodoPlatform/komodo-defi-sdk-flutter.git" source: git version: "0.2.0+0" diff --git a/test_units/tests/utils/transaction_history/sanitize_transaction_test.dart b/test_units/tests/utils/transaction_history/sanitize_transaction_test.dart index 8a2641a54a..38c8147250 100644 --- a/test_units/tests/utils/transaction_history/sanitize_transaction_test.dart +++ b/test_units/tests/utils/transaction_history/sanitize_transaction_test.dart @@ -38,6 +38,28 @@ void testSanitizeTransaction() { ); } + group('Self-transfer edge case', () { + test( + 'leaves address untouched when to and from are identical (self-transfer)', + () { + final selfAddress = 'self1'; + final tx = createTransaction( + from: [selfAddress], + to: [selfAddress], + ); + final walletAddresses = {selfAddress}; + + final result = tx.sanitize(walletAddresses); + + // The address should remain in both to and from + expect(result.to, equals([selfAddress])); + expect(result.from, equals([selfAddress])); + // There should never be 0 to or from addresses + expect(result.to, isNotEmpty); + expect(result.from, isNotEmpty); + }); + }); + group('Basic functionality', () { test('returns original transaction when from list is empty', () { final tx = createTransaction(from: [], to: ['addr1', 'addr2']); @@ -83,7 +105,7 @@ void testSanitizeTransaction() { final result = tx.sanitize(walletAddresses); - expect(result.to, isEmpty); + expect(result.to, ['sender1']); }); test('handles case where to list is empty', () {