diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index 60dd1bc68..1766bbe86 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -77,14 +77,6 @@ jobs: run: | cd /opt/android/cake_wallet flutter packages pub run tool/generate_localization.dart - - - name: Upgrade flutter packages - run: | - cd /opt/android/cake_wallet - cd cw_core && flutter pub upgrade && cd .. - cd cw_monero && flutter pub upgrade && cd .. - cd cw_bitcoin && flutter pub upgrade && cd .. - cd cw_haven && flutter pub upgrade && cd .. - name: Build generated code run: | diff --git a/lib/entities/default_settings_migration.dart b/lib/entities/default_settings_migration.dart index 3c8d9fbbe..b1bb144be 100644 --- a/lib/entities/default_settings_migration.dart +++ b/lib/entities/default_settings_migration.dart @@ -1,10 +1,12 @@ import 'dart:io' show File, Platform; import 'package:cake_wallet/bitcoin/bitcoin.dart'; +import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cw_core/pathForWallet.dart'; import 'package:cake_wallet/entities/secret_store_key.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:hive/hive.dart'; +import 'package:share_plus/share_plus.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cw_core/wallet_type.dart'; @@ -142,7 +144,9 @@ Future defaultSettingsMigration( case 19: await validateBitcoinSavedTransactionPriority(sharedPreferences); break; - + case 20: + await migrateExchangeStatus(sharedPreferences); + break; default: break; } @@ -501,3 +505,13 @@ Future changeDefaultHavenNode( await node.save(); }); } + +Future migrateExchangeStatus(SharedPreferences sharedPreferences) async { + final isExchangeDisabled = sharedPreferences.getBool(PreferencesKey.disableExchangeKey); + if (isExchangeDisabled == null) { + return; + } + + await sharedPreferences.setInt(PreferencesKey.exchangeStatusKey, isExchangeDisabled + ? ExchangeApiMode.disabled.raw : ExchangeApiMode.enabled.raw); +} diff --git a/lib/entities/exchange_api_mode.dart b/lib/entities/exchange_api_mode.dart new file mode 100644 index 000000000..0b8b575a5 --- /dev/null +++ b/lib/entities/exchange_api_mode.dart @@ -0,0 +1,39 @@ +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cw_core/enumerable_item.dart'; + +class ExchangeApiMode extends EnumerableItem with Serializable { + const ExchangeApiMode({required String title, required int raw}) : super(title: title, raw: raw); + + static const all = [ExchangeApiMode.enabled, ExchangeApiMode.torOnly, ExchangeApiMode.disabled]; + + static const enabled = ExchangeApiMode(raw: 0, title: 'Enabled'); + static const torOnly = ExchangeApiMode(raw: 1, title: 'Tor only'); + static const disabled = ExchangeApiMode(raw: 2, title: 'Disabled'); + + static ExchangeApiMode deserialize({required int raw}) { + switch (raw) { + case 0: + return enabled; + case 1: + return torOnly; + case 2: + return disabled; + default: + throw Exception('Unexpected token: $raw for ExchangeApiMode deserialize'); + } + } + + @override + String toString() { + switch (this) { + case ExchangeApiMode.enabled: + return S.current.enabled; + case ExchangeApiMode.torOnly: + return S.current.tor_only; + case ExchangeApiMode.disabled: + return S.current.disabled; + default: + return ''; + } + } +} \ No newline at end of file diff --git a/lib/exchange/exchange_provider.dart b/lib/exchange/exchange_provider.dart index 99e6da913..cc81a21f6 100644 --- a/lib/exchange/exchange_provider.dart +++ b/lib/exchange/exchange_provider.dart @@ -14,7 +14,7 @@ abstract class ExchangeProvider { bool get isAvailable; bool get isEnabled; bool get supportsFixedRate; - bool get shouldUseOnionAddress => false; + bool get supportsOnionAddress => false; @override String toString() => title; diff --git a/lib/exchange/trocador/trocador_exchange_provider.dart b/lib/exchange/trocador/trocador_exchange_provider.dart index 49503a1d0..750ffb98a 100644 --- a/lib/exchange/trocador/trocador_exchange_provider.dart +++ b/lib/exchange/trocador/trocador_exchange_provider.dart @@ -13,9 +13,11 @@ import 'package:cake_wallet/.secrets.g.dart' as secrets; import 'package:http/http.dart'; class TrocadorExchangeProvider extends ExchangeProvider { - TrocadorExchangeProvider() + TrocadorExchangeProvider({this.useTorOnly = false}) : _lastUsedRateId = '', super(pairList: _supportedPairs()); + + bool useTorOnly; static const List _notSupported = [ CryptoCurrency.scrt, @@ -83,7 +85,7 @@ class TrocadorExchangeProvider extends ExchangeProvider { params['id'] = _lastUsedRateId; } - final String apiAuthority = shouldUseOnionAddress ? await _getAuthority() : clearNetAuthority; + final String apiAuthority = await _getAuthority(); final uri = Uri.https(apiAuthority, createTradePath, params); final response = await get(uri); @@ -142,7 +144,7 @@ class TrocadorExchangeProvider extends ExchangeProvider { 'name': from.name, }; - final String apiAuthority = shouldUseOnionAddress ? await _getAuthority() : clearNetAuthority; + final String apiAuthority = await _getAuthority(); final uri = Uri.https(apiAuthority, coinPath, params); final response = await get(uri); @@ -178,7 +180,7 @@ class TrocadorExchangeProvider extends ExchangeProvider { return 0.0; } - final String apiAuthority = shouldUseOnionAddress ? await _getAuthority() : clearNetAuthority; + final String apiAuthority = await _getAuthority(); final params = { 'api_key': apiKey, @@ -214,7 +216,7 @@ class TrocadorExchangeProvider extends ExchangeProvider { @override Future findTradeById({required String id}) async { - final String apiAuthority = shouldUseOnionAddress ? await _getAuthority() : clearNetAuthority; + final String apiAuthority = await _getAuthority(); final uri = Uri.https(apiAuthority, tradePath, {'api_key': apiKey, 'id': id}); return get(uri).then((response) { if (response.statusCode != 200) { @@ -265,7 +267,7 @@ class TrocadorExchangeProvider extends ExchangeProvider { bool get supportsFixedRate => true; @override - bool get shouldUseOnionAddress => true; + bool get supportsOnionAddress => true; @override String get title => 'Trocador'; @@ -297,11 +299,21 @@ class TrocadorExchangeProvider extends ExchangeProvider { } Future _getAuthority() async { + if(!supportsOnionAddress){ + return clearNetAuthority; + } + try { + if (useTorOnly) { + return onionApiAuthority; + } + final uri = Uri.https(onionApiAuthority, '/api/trade'); await get(uri); + return onionApiAuthority; } catch (e) { + return clearNetAuthority; } } diff --git a/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart b/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart index 7d720e4e5..42ba0debb 100644 --- a/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart +++ b/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cake_wallet/src/screens/nodes/widgets/node_form.dart'; import 'package:cake_wallet/src/screens/settings/widgets/settings_choices_cell.dart'; @@ -48,43 +49,55 @@ class _AdvancedPrivacySettingsBodyState extends State Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SettingsChoicesCell( - ChoicesListItem( - title: S.current.fiat_api, - items: FiatApiMode.all, - selectedItem: widget.privacySettingsViewModel.fiatApi, - onItemSelected: (FiatApiMode mode) => - widget.privacySettingsViewModel.setFiatMode(mode), - ), - ), - SettingsChoicesCell( - ChoicesListItem( - title: S.current.exchange, - items: FiatApiMode.all, - selectedItem: widget.privacySettingsViewModel.exchangeStatus, - onItemSelected: (FiatApiMode mode) => - widget.privacySettingsViewModel.setEnableExchange(mode), - ), - ), - SettingsSwitcherCell( - title: S.current.add_custom_node, - value: widget.privacySettingsViewModel.addCustomNode, - onValueChange: (_, __) => widget.privacySettingsViewModel.toggleAddCustomNode(), - ), - if (widget.privacySettingsViewModel.addCustomNode) - Padding( - padding: EdgeInsets.only(left: 24, right: 24, top: 24), - child: NodeForm( - formKey: _formKey, - nodeViewModel: widget.nodeViewModel, + content: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Observer( + builder: (_) { + return SettingsSwitcherCell( + title: S.current.disable_fiat, + value: widget.privacySettingsViewModel.fiatApi == FiatApiMode.disabled, + onValueChange: (BuildContext context, bool value) { + widget.privacySettingsViewModel.setFiatMode(value); + }); + } + ), + Observer( + builder: (_) { + return SettingsChoicesCell( + ChoicesListItem( + title: S.current.exchange, + items: ExchangeApiMode.all, + selectedItem: widget.privacySettingsViewModel.exchangeStatus, + onItemSelected: (ExchangeApiMode mode) => + widget.privacySettingsViewModel.setEnableExchange(mode), ), - ) - ], - ), + ); + } + ), + Observer( + builder: (_) { + return Column( + children: [ + SettingsSwitcherCell( + title: S.current.add_custom_node, + value: widget.privacySettingsViewModel.addCustomNode, + onValueChange: (_, __) => widget.privacySettingsViewModel.toggleAddCustomNode(), + ), + if (widget.privacySettingsViewModel.addCustomNode) + Padding( + padding: EdgeInsets.only(left: 24, right: 24, top: 24), + child: NodeForm( + formKey: _formKey, + nodeViewModel: widget.nodeViewModel, + ), + ) + ], + ); + } + ), + + ], ), bottomSectionPadding: EdgeInsets.all(24), bottomSection: Column( diff --git a/lib/src/screens/settings/privacy_page.dart b/lib/src/screens/settings/privacy_page.dart index 974f9a3d7..cc6acc826 100644 --- a/lib/src/screens/settings/privacy_page.dart +++ b/lib/src/screens/settings/privacy_page.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; @@ -24,20 +25,18 @@ class PrivacyPage extends BasePage { return Column( mainAxisSize: MainAxisSize.min, children: [ + SettingsSwitcherCell( + title: S.current.disable_fiat, + value: _privacySettingsViewModel.isFiatDisabled, + onValueChange: (BuildContext context, bool value) { + _privacySettingsViewModel.setFiatMode(value); + }), SettingsChoicesCell( - ChoicesListItem( - title: S.current.fiat_api, - items: FiatApiMode.all, - selectedItem: _privacySettingsViewModel.fiatApi, - onItemSelected: (FiatApiMode mode) => _privacySettingsViewModel.setFiatMode(mode), - ), - ), - SettingsChoicesCell( - ChoicesListItem( + ChoicesListItem( title: S.current.exchange, - items: FiatApiMode.all, + items: ExchangeApiMode.all, selectedItem: _privacySettingsViewModel.exchangeStatus, - onItemSelected: (FiatApiMode mode) => _privacySettingsViewModel.setEnableExchange(mode), + onItemSelected: (ExchangeApiMode mode) => _privacySettingsViewModel.setEnableExchange(mode), ), ), SettingsSwitcherCell( diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart index 989fdae33..b6e5a7549 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -1,4 +1,5 @@ import 'package:cake_wallet/bitcoin/bitcoin.dart'; +import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cake_wallet/entities/pin_code_required_duration.dart'; import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cw_core/transaction_priority.dart'; @@ -31,7 +32,7 @@ abstract class SettingsStoreBase with Store { required bool initialSaveRecipientAddress, required FiatApiMode initialFiatMode, required bool initialAllowBiometricalAuthentication, - required FiatApiMode initialExchangeStatus, + required ExchangeApiMode initialExchangeStatus, required ThemeBase initialTheme, required int initialPinLength, required String initialLanguageCode, @@ -154,7 +155,7 @@ abstract class SettingsStoreBase with Store { reaction( (_) => exchangeStatus, - (FiatApiMode mode) => sharedPreferences.setInt( + (ExchangeApiMode mode) => sharedPreferences.setInt( PreferencesKey.exchangeStatusKey, mode.serialize())); this @@ -192,7 +193,7 @@ abstract class SettingsStoreBase with Store { bool allowBiometricalAuthentication; @observable - FiatApiMode exchangeStatus; + ExchangeApiMode exchangeStatus; @observable ThemeBase currentTheme; @@ -284,9 +285,9 @@ abstract class SettingsStoreBase with Store { final allowBiometricalAuthentication = sharedPreferences .getBool(PreferencesKey.allowBiometricalAuthenticationKey) ?? false; - final exchangeStatus = FiatApiMode.deserialize( + final exchangeStatus = ExchangeApiMode.deserialize( raw: sharedPreferences - .getInt(PreferencesKey.exchangeStatusKey) ?? FiatApiMode.enabled.raw); + .getInt(PreferencesKey.exchangeStatusKey) ?? ExchangeApiMode.enabled.raw); final legacyTheme = (sharedPreferences.getBool(PreferencesKey.isDarkThemeLegacy) ?? false) ? ThemeType.dark.index @@ -401,9 +402,9 @@ abstract class SettingsStoreBase with Store { allowBiometricalAuthentication = sharedPreferences .getBool(PreferencesKey.allowBiometricalAuthenticationKey) ?? allowBiometricalAuthentication; - exchangeStatus = FiatApiMode.deserialize( + exchangeStatus = ExchangeApiMode.deserialize( raw: sharedPreferences - .getInt(PreferencesKey.exchangeStatusKey) ?? FiatApiMode.enabled.raw); + .getInt(PreferencesKey.exchangeStatusKey) ?? ExchangeApiMode.enabled.raw); final legacyTheme = (sharedPreferences.getBool(PreferencesKey.isDarkThemeLegacy) ?? false) ? ThemeType.dark.index diff --git a/lib/view_model/advanced_privacy_settings_view_model.dart b/lib/view_model/advanced_privacy_settings_view_model.dart index 7d526047d..1bc2ecd9f 100644 --- a/lib/view_model/advanced_privacy_settings_view_model.dart +++ b/lib/view_model/advanced_privacy_settings_view_model.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cake_wallet/store/settings_store.dart'; import 'package:cw_core/wallet_type.dart'; @@ -12,7 +13,7 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store { AdvancedPrivacySettingsViewModelBase(this.type, this._settingsStore) : _addCustomNode = false; @computed - FiatApiMode get exchangeStatus => _settingsStore.exchangeStatus; + ExchangeApiMode get exchangeStatus => _settingsStore.exchangeStatus; @computed FiatApiMode get fiatApi => _settingsStore.fiatApiMode; @@ -28,12 +29,16 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store { bool get addCustomNode => _addCustomNode; @action - void setFiatMode(FiatApiMode value) { - _settingsStore.fiatApiMode = value; + void setFiatMode(bool value) { + if (value) { + _settingsStore.fiatApiMode = FiatApiMode.disabled; + return; + } + _settingsStore.fiatApiMode = FiatApiMode.enabled; } @action - void setEnableExchange(FiatApiMode value) { + void setEnableExchange(ExchangeApiMode value) { _settingsStore.exchangeStatus = value; } diff --git a/lib/view_model/exchange/exchange_view_model.dart b/lib/view_model/exchange/exchange_view_model.dart index 7921115c6..20a5a8e76 100644 --- a/lib/view_model/exchange/exchange_view_model.dart +++ b/lib/view_model/exchange/exchange_view_model.dart @@ -56,6 +56,7 @@ abstract class ExchangeViewModelBase with Store { isDepositAddressEnabled = false, isReceiveAddressEnabled = false, isReceiveAmountEditable = false, + _providerUseTorOnly = false, receiveCurrencies = [], depositCurrencies = [], limits = Limits(min: 0, max: 0), @@ -65,6 +66,7 @@ abstract class ExchangeViewModelBase with Store { depositCurrency = wallet.currency, providerList = [], selectedProviders = ObservableList() { + _providerUseTorOnly = _settingsStore.exchangeStatus == FiatApiMode.torOnly; _setProviders(); const excludeDepositCurrencies = [CryptoCurrency.btt, CryptoCurrency.nano]; const excludeReceiveCurrencies = [CryptoCurrency.xlm, CryptoCurrency.xrp, @@ -121,18 +123,18 @@ abstract class ExchangeViewModelBase with Store { _calculateBestRate(); }); } - + bool _providerUseTorOnly; final WalletBase wallet; final Box trades; final ExchangeTemplateStore _exchangeTemplateStore; final TradesStore tradesStore; final SharedPreferences sharedPreferences; - final _allProviders = [ + List get _allProviders => [ ChangeNowExchangeProvider(), SideShiftExchangeProvider(), SimpleSwapExchangeProvider(), - TrocadorExchangeProvider(), + TrocadorExchangeProvider(useTorOnly: _providerUseTorOnly), ]; @observable @@ -695,7 +697,7 @@ abstract class ExchangeViewModelBase with Store { void _setProviders(){ if (_settingsStore.exchangeStatus == FiatApiMode.torOnly) { - providerList = _allProviders.where((provider) => provider.shouldUseOnionAddress).toList(); + providerList = _allProviders.where((provider) => provider.supportsOnionAddress).toList(); } else { providerList = _allProviders; } diff --git a/lib/view_model/settings/privacy_settings_view_model.dart b/lib/view_model/settings/privacy_settings_view_model.dart index db345d0f2..1919b1944 100644 --- a/lib/view_model/settings/privacy_settings_view_model.dart +++ b/lib/view_model/settings/privacy_settings_view_model.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cake_wallet/store/settings_store.dart'; import 'package:mobx/mobx.dart'; import 'package:cake_wallet/entities/fiat_api_mode.dart'; @@ -12,23 +13,27 @@ abstract class PrivacySettingsViewModelBase with Store { final SettingsStore _settingsStore; @computed - FiatApiMode get exchangeStatus => _settingsStore.exchangeStatus; + ExchangeApiMode get exchangeStatus => _settingsStore.exchangeStatus; @computed bool get shouldSaveRecipientAddress => _settingsStore.shouldSaveRecipientAddress; @computed - FiatApiMode get fiatApi => _settingsStore.fiatApiMode; + bool get isFiatDisabled => _settingsStore.fiatApiMode == FiatApiMode.disabled; @action void setShouldSaveRecipientAddress(bool value) => _settingsStore.shouldSaveRecipientAddress = value; @action - void setEnableExchange(FiatApiMode value) => _settingsStore.exchangeStatus = value; + void setEnableExchange(ExchangeApiMode value) => _settingsStore.exchangeStatus = value; @action - void setFiatMode(FiatApiMode value) { - _settingsStore.fiatApiMode = value; + void setFiatMode(bool value) { + if (value) { + _settingsStore.fiatApiMode = FiatApiMode.disabled; + return; + } + _settingsStore.fiatApiMode = FiatApiMode.enabled; } }