From 032a8c8c3387ab9cb87d54fc4aa21977dc30266a Mon Sep 17 00:00:00 2001 From: Godwin Asuquo Date: Mon, 6 Feb 2023 21:20:43 +0200 Subject: [PATCH 01/21] Add trocador exchange provider --- assets/images/trocador.png | Bin 0 -> 10417 bytes lib/entities/preferences_key.dart | 1 + .../changenow_exchange_provider.dart | 1 + lib/exchange/exchange_provider.dart | 1 + .../exchange_provider_description.dart | 27 +- .../trocador/trocador_exchange_provider.dart | 268 ++++++++++++++++++ lib/exchange/trocador/trocador_request.dart | 21 ++ .../screens/dashboard/widgets/trade_row.dart | 75 +++-- .../advanced_privacy_settings_page.dart | 13 +- lib/src/screens/settings/privacy_page.dart | 31 +- lib/store/dashboard/trade_filter_store.dart | 16 +- lib/store/settings_store.dart | 23 +- .../advanced_privacy_settings_view_model.dart | 37 ++- .../dashboard/dashboard_view_model.dart | 8 +- .../exchange/exchange_trade_view_model.dart | 4 + .../exchange/exchange_view_model.dart | 16 +- .../settings/privacy_settings_view_model.dart | 14 +- lib/view_model/trade_details_view_model.dart | 4 + tool/utils/secret_key.dart | 1 + 19 files changed, 453 insertions(+), 108 deletions(-) create mode 100644 assets/images/trocador.png create mode 100644 lib/exchange/trocador/trocador_exchange_provider.dart create mode 100644 lib/exchange/trocador/trocador_request.dart diff --git a/assets/images/trocador.png b/assets/images/trocador.png new file mode 100644 index 0000000000000000000000000000000000000000..67c9f221c8586f309e22acf4853735ffdd4c3e15 GIT binary patch literal 10417 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 false; @override String toString() => title; diff --git a/lib/exchange/exchange_provider_description.dart b/lib/exchange/exchange_provider_description.dart index 2fd231085..e545f69ce 100644 --- a/lib/exchange/exchange_provider_description.dart +++ b/lib/exchange/exchange_provider_description.dart @@ -1,31 +1,30 @@ import 'package:cw_core/enumerable_item.dart'; -class ExchangeProviderDescription extends EnumerableItem - with Serializable { - const ExchangeProviderDescription({ - required String title, - required int raw, - required this.image, - this.horizontalLogo = false}) +class ExchangeProviderDescription extends EnumerableItem with Serializable { + const ExchangeProviderDescription( + {required String title, required int raw, required this.image, this.horizontalLogo = false}) : super(title: title, raw: raw); final bool horizontalLogo; final String image; - static const xmrto = ExchangeProviderDescription(title: 'XMR.TO', raw: 0, image: 'assets/images/xmrto.png'); + static const xmrto = + ExchangeProviderDescription(title: 'XMR.TO', raw: 0, image: 'assets/images/xmrto.png'); static const changeNow = ExchangeProviderDescription(title: 'ChangeNOW', raw: 1, image: 'assets/images/changenow.png'); static const morphToken = ExchangeProviderDescription(title: 'MorphToken', raw: 2, image: 'assets/images/morph.png'); - static const sideShift = + static const sideShift = ExchangeProviderDescription(title: 'SideShift', raw: 3, image: 'assets/images/sideshift.png'); - static const simpleSwap = - ExchangeProviderDescription(title: 'SimpleSwap', raw: 4, image: 'assets/images/simpleSwap.png'); + static const simpleSwap = ExchangeProviderDescription( + title: 'SimpleSwap', raw: 4, image: 'assets/images/simpleSwap.png'); - static const all = - ExchangeProviderDescription(title: 'All trades', raw: 5, image:''); + static const trocador = + ExchangeProviderDescription(title: 'Trocador', raw: 5, image: 'assets/images/trocador.png'); + + static const all = ExchangeProviderDescription(title: 'All trades', raw: 6, image: ''); static ExchangeProviderDescription deserialize({required int raw}) { switch (raw) { @@ -40,6 +39,8 @@ class ExchangeProviderDescription extends EnumerableItem case 4: return simpleSwap; case 5: + return trocador; + case 6: return all; default: throw Exception('Unexpected token: $raw for ExchangeProviderDescription deserialize'); diff --git a/lib/exchange/trocador/trocador_exchange_provider.dart b/lib/exchange/trocador/trocador_exchange_provider.dart new file mode 100644 index 000000000..18ea27895 --- /dev/null +++ b/lib/exchange/trocador/trocador_exchange_provider.dart @@ -0,0 +1,268 @@ +import 'dart:convert'; + +import 'package:cake_wallet/exchange/exchange_pair.dart'; +import 'package:cake_wallet/exchange/exchange_provider.dart'; +import 'package:cake_wallet/exchange/trade_state.dart'; +import 'package:cake_wallet/exchange/trocador/trocador_request.dart'; +import 'package:cw_core/crypto_currency.dart'; +import 'package:cake_wallet/exchange/trade_request.dart'; +import 'package:cake_wallet/exchange/trade.dart'; +import 'package:cake_wallet/exchange/limits.dart'; +import 'package:cake_wallet/exchange/exchange_provider_description.dart'; +import 'package:cake_wallet/.secrets.g.dart' as secrets; +import 'package:http/http.dart'; + +class TrocadorExchangeProvider extends ExchangeProvider { + TrocadorExchangeProvider() + : _lastUsedRateId = '', + super(pairList: _supportedPairs()); + + static const List _notSupported = [ + CryptoCurrency.xhv, + CryptoCurrency.dcr, + CryptoCurrency.oxt, + CryptoCurrency.pivx, + CryptoCurrency.scrt, + CryptoCurrency.stx, + CryptoCurrency.bttc, + CryptoCurrency.zaddr, + CryptoCurrency.usdcpoly, + CryptoCurrency.maticpoly, + ]; + + static List _supportedPairs() { + final supportedCurrencies = + CryptoCurrency.all.where((element) => !_notSupported.contains(element)).toList(); + + return supportedCurrencies + .map((i) => supportedCurrencies.map((k) => ExchangePair(from: i, to: k, reverse: true))) + .expand((i) => i) + .toList(); + } + + static const onionApiAuthority = 'trocadorfyhlu27aefre5u7zri66gudtzdyelymftvr4yjwcxhfaqsid.onion'; + static const clearNetAuthority = 'trocador.app'; + static const apiKey = secrets.trocadorApiKey; + static const newRatePath = '/api/new_rate'; + static const createTradePath = 'api/new_trade'; + static const tradePath = 'api/trade'; + String _lastUsedRateId; + + @override + Future checkIsAvailable() async => true; + + @override + Future createTrade({required TradeRequest request, required bool isFixedRateMode}) { + final _request = request as TrocadorRequest; + return _createTrade(request: _request, isFixedRateMode: isFixedRateMode); + } + + Future _createTrade({ + required TrocadorRequest request, + required bool isFixedRateMode, + }) async { + final params = { + 'api_key': apiKey, + 'ticker_from': request.from.title.toLowerCase(), + 'ticker_to': request.to.title.toLowerCase(), + 'network_from': _networkFor(request.from), + 'network_to': _networkFor(request.to), + 'payment': isFixedRateMode ? 'True' : 'False', + 'min_kycrating': 'C', + 'markup': '3', + 'best_only': 'True', + if (!isFixedRateMode) 'amount_from': request.fromAmount, + if (isFixedRateMode) 'amount_to': request.toAmount, + 'address': request.address, + 'refund': request.refundAddress + }; + + if (isFixedRateMode) { + await fetchRate( + from: request.from, + to: request.to, + amount: double.tryParse(request.toAmount) ?? 0, + isFixedRateMode: true, + isReceiveAmount: true, + ); + params['id'] = _lastUsedRateId; + } + + final String apiAuthority = shouldUseOnionAddress ? await _getAuthority() : clearNetAuthority; + + final uri = Uri.https(apiAuthority, createTradePath, params); + final response = await get(uri); + + if (response.statusCode == 400) { + final responseJSON = json.decode(response.body) as Map; + final error = responseJSON['error'] as String; + final message = responseJSON['message'] as String; + throw Exception('${error}\n$message'); + } + + if (response.statusCode != 200) { + throw Exception('Unexpected http status: ${response.statusCode}'); + } + + final responseJSON = json.decode(response.body) as Map; + final id = responseJSON['trade_id'] as String; + final inputAddress = responseJSON['address_provider'] as String; + final refundAddress = responseJSON['refund_address'] as String; + final status = responseJSON['status'] as String; + final state = TradeState.deserialize(raw: status); + final payoutAddress = responseJSON['address_user'] as String; + final date = responseJSON['date'] as String; + + return Trade( + id: id, + from: request.from, + to: request.to, + provider: description, + inputAddress: inputAddress, + refundAddress: refundAddress, + state: state, + createdAt: DateTime.tryParse(date)?.toLocal(), + amount: responseJSON['amount_from']?.toString() ?? request.fromAmount, + payoutAddress: payoutAddress); + } + + @override + ExchangeProviderDescription get description => ExchangeProviderDescription.trocador; + + @override + Future fetchLimits( + {required CryptoCurrency from, + required CryptoCurrency to, + required bool isFixedRateMode}) async { + //TODO: implement limits from trocador api + return Limits( + min: 0.0, + ); + } + + @override + Future fetchRate( + {required CryptoCurrency from, + required CryptoCurrency to, + required double amount, + required bool isFixedRateMode, + required bool isReceiveAmount}) async { + try { + if (amount == 0) { + return 0.0; + } + + final String apiAuthority = shouldUseOnionAddress ? await _getAuthority() : clearNetAuthority; + + final params = { + 'api_key': apiKey, + 'ticker_from': from.title.toLowerCase(), + 'ticker_to': to.title.toLowerCase(), + 'network_from': _networkFor(from), + 'network_to': _networkFor(to), + 'amount_from': amount.toString(), + 'amount_to': amount.toString(), + 'payment': isFixedRateMode ? 'True' : 'False', + 'min_kycrating': 'C', + 'markup': '3', + 'best_only': 'True', + }; + + final uri = Uri.https(apiAuthority, newRatePath, params); + final response = await get(uri); + final responseJSON = json.decode(response.body) as Map; + final fromAmount = double.parse(responseJSON['amount_from'].toString()); + final toAmount = double.parse(responseJSON['amount_to'].toString()); + final rateId = responseJSON['trade_id'] as String? ?? ''; + + if (rateId.isNotEmpty) { + _lastUsedRateId = rateId; + } + + return isReceiveAmount ? (amount / fromAmount) : (toAmount / amount); + } catch (e) { + print(e.toString()); + return 0.0; + } + } + + @override + Future findTradeById({required String id}) async { + final String apiAuthority = shouldUseOnionAddress ? await _getAuthority() : clearNetAuthority; + final uri = Uri.https(apiAuthority, tradePath, {'api_key': apiKey, 'id': id}); + return get(uri).then((response) { + if (response.statusCode != 200) { + throw Exception('Unexpected http status: ${response.statusCode}'); + } + + final responseListJson = json.decode(response.body) as List; + + final responseJSON = responseListJson.first; + final id = responseJSON['trade_id'] as String; + final inputAddress = responseJSON['address_user'] as String; + final refundAddress = responseJSON['refund_address'] as String; + final payoutAddress = responseJSON['address_provider'] as String; + final fromAmount = responseJSON['amount_from']?.toString() ?? '0'; + final from = CryptoCurrency.fromString(responseJSON['ticker_from'] as String); + final to = CryptoCurrency.fromString(responseJSON['ticker_to'] as String); + final state = TradeState.deserialize(raw: responseJSON['status'] as String); + final date = DateTime.parse(responseJSON['date'] as String); + + return Trade( + id: id, + from: from, + to: to, + provider: description, + inputAddress: inputAddress, + refundAddress: refundAddress, + createdAt: date, + amount: fromAmount, + state: state, + payoutAddress: payoutAddress, + ); + }); + } + + @override + bool get isAvailable => true; + + @override + bool get isEnabled => true; + + @override + bool get supportsFixedRate => true; + + @override + bool get shouldUseOnionAddress => true; + + @override + String get title => 'Trocador'; + + String _networkFor(CryptoCurrency currency) { + switch (currency) { + case CryptoCurrency.usdt: + return CryptoCurrency.btc.title.toLowerCase(); + default: + return currency.tag != null ? _normalizeTag(currency.tag!) : 'Mainnet'; + } + } + + String _normalizeTag(String tag) { + switch (tag) { + case 'ETH': + return 'ERC20'; + default: + return tag.toLowerCase(); + } + } + + Future _getAuthority() async { + try { + final uri = Uri.https(onionApiAuthority, '/api/trade'); + await get(uri); + return onionApiAuthority; + } catch (e) { + return clearNetAuthority; + } + } +} diff --git a/lib/exchange/trocador/trocador_request.dart b/lib/exchange/trocador/trocador_request.dart new file mode 100644 index 000000000..fbb8fdc84 --- /dev/null +++ b/lib/exchange/trocador/trocador_request.dart @@ -0,0 +1,21 @@ +import 'package:cake_wallet/exchange/trade_request.dart'; +import 'package:cw_core/crypto_currency.dart'; + +class TrocadorRequest extends TradeRequest { + TrocadorRequest( + {required this.from, + required this.to, + required this.address, + required this.fromAmount, + required this.toAmount, + required this.refundAddress, + required this.isReverse}); + + CryptoCurrency from; + CryptoCurrency to; + String address; + String fromAmount; + String toAmount; + String refundAddress; + bool isReverse; +} diff --git a/lib/src/screens/dashboard/widgets/trade_row.dart b/lib/src/screens/dashboard/widgets/trade_row.dart index 21952140e..3b613b0e2 100644 --- a/lib/src/screens/dashboard/widgets/trade_row.dart +++ b/lib/src/screens/dashboard/widgets/trade_row.dart @@ -9,7 +9,8 @@ class TradeRow extends StatelessWidget { required this.to, required this.createdAtFormattedDate, this.onTap, - this.formattedAmount,}); + this.formattedAmount, + }); final VoidCallback? onTap; final ExchangeProviderDescription provider; @@ -35,47 +36,40 @@ class TradeRow extends StatelessWidget { SizedBox(width: 12), Expanded( child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text('${from.toString()} → ${to.toString()}', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: Theme.of(context).accentTextTheme!.headline2!.backgroundColor! - )), - formattedAmount != null - ? Text(formattedAmount! + ' ' + amountCrypto, - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: Theme.of(context).accentTextTheme!.headline2!.backgroundColor! - )) - : Container() - ]), - SizedBox(height: 5), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - if (createdAtFormattedDate != null) - Text(createdAtFormattedDate!, - style: TextStyle( - fontSize: 14, - color: Theme.of(context).textTheme! - .overline!.backgroundColor!)) - ]) - ], - ) - ) + mainAxisSize: MainAxisSize.min, + children: [ + Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ + Text('${from.toString()} → ${to.toString()}', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + color: Theme.of(context).accentTextTheme!.headline2!.backgroundColor!)), + formattedAmount != null + ? Text(formattedAmount! + ' ' + amountCrypto, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + color: + Theme.of(context).accentTextTheme!.headline2!.backgroundColor!)) + : Container() + ]), + SizedBox(height: 5), + Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ + if (createdAtFormattedDate != null) + Text(createdAtFormattedDate!, + style: TextStyle( + fontSize: 14, + color: Theme.of(context).textTheme!.overline!.backgroundColor!)) + ]) + ], + )) ], ), )); } - Image? _getPoweredImage(ExchangeProviderDescription provider) { - Image? image; + Widget? _getPoweredImage(ExchangeProviderDescription provider) { + Widget? image; switch (provider) { case ExchangeProviderDescription.xmrto: @@ -93,10 +87,15 @@ class TradeRow extends StatelessWidget { case ExchangeProviderDescription.simpleSwap: image = Image.asset('assets/images/simpleSwap.png', width: 36, height: 36); break; + case ExchangeProviderDescription.trocador: + image = ClipRRect( + borderRadius: BorderRadius.circular(50), + child: Image.asset('assets/images/trocador.png', width: 36, height: 36)); + break; default: image = null; } return image; } -} \ No newline at end of file +} 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 6c2f996df..2a9f4d291 100644 --- a/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart +++ b/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart @@ -1,7 +1,11 @@ +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'; import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart'; import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart'; import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart'; +import 'package:cake_wallet/view_model/settings/choices_list_item.dart'; +import 'package:cake_wallet/view_model/settings/switcher_list_item.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/generated/i18n.dart'; @@ -48,9 +52,14 @@ class _AdvancedPrivacySettingsBodyState extends State>().map( (item) => Observer( - builder: (_) => SettingsSwitcherCell( + builder: (_) => SettingsChoicesCell(item) + ), + ), + ...widget.privacySettingsViewModel.settings.whereType().map( + (item) => Observer( + builder: (_) => SettingsSwitcherCell( title: item.title, value: item.value(), onValueChange: item.onValueChange, diff --git a/lib/src/screens/settings/privacy_page.dart b/lib/src/screens/settings/privacy_page.dart index 2f15cc225..974f9a3d7 100644 --- a/lib/src/screens/settings/privacy_page.dart +++ b/lib/src/screens/settings/privacy_page.dart @@ -1,6 +1,9 @@ +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'; +import 'package:cake_wallet/src/screens/settings/widgets/settings_choices_cell.dart'; import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart'; +import 'package:cake_wallet/view_model/settings/choices_list_item.dart'; import 'package:cake_wallet/view_model/settings/privacy_settings_view_model.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; @@ -21,18 +24,22 @@ 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); - }), - SettingsSwitcherCell( - title: S.current.disable_exchange, - value: _privacySettingsViewModel.disableExchange, - onValueChange: (BuildContext context, bool value) { - _privacySettingsViewModel.setEnableExchange(value); - }), + SettingsChoicesCell( + ChoicesListItem( + title: S.current.fiat_api, + items: FiatApiMode.all, + selectedItem: _privacySettingsViewModel.fiatApi, + onItemSelected: (FiatApiMode mode) => _privacySettingsViewModel.setFiatMode(mode), + ), + ), + SettingsChoicesCell( + ChoicesListItem( + title: S.current.exchange, + items: FiatApiMode.all, + selectedItem: _privacySettingsViewModel.exchangeStatus, + onItemSelected: (FiatApiMode mode) => _privacySettingsViewModel.setEnableExchange(mode), + ), + ), SettingsSwitcherCell( title: S.current.settings_save_recipient_address, value: _privacySettingsViewModel.shouldSaveRecipientAddress, diff --git a/lib/store/dashboard/trade_filter_store.dart b/lib/store/dashboard/trade_filter_store.dart index 87fa749a9..c772a35d6 100644 --- a/lib/store/dashboard/trade_filter_store.dart +++ b/lib/store/dashboard/trade_filter_store.dart @@ -12,7 +12,8 @@ abstract class TradeFilterStoreBase with Store { displayChangeNow = true, displaySideShift = true, displayMorphToken = true, - displaySimpleSwap = true; + displaySimpleSwap = true, + displayTrocador = true; @observable bool displayXMRTO; @@ -29,8 +30,11 @@ abstract class TradeFilterStoreBase with Store { @observable bool displaySimpleSwap; + @observable + bool displayTrocador; + @computed - bool get displayAllTrades => displayChangeNow && displaySideShift && displaySimpleSwap; + bool get displayAllTrades => displayChangeNow && displaySideShift && displaySimpleSwap && displayTrocador; @action void toggleDisplayExchange(ExchangeProviderDescription provider) { @@ -50,6 +54,9 @@ abstract class TradeFilterStoreBase with Store { case ExchangeProviderDescription.morphToken: displayMorphToken = !displayMorphToken; break; + case ExchangeProviderDescription.trocador: + displayTrocador = !displayTrocador; + break; case ExchangeProviderDescription.all: if (displayAllTrades) { displayChangeNow = false; @@ -57,12 +64,14 @@ abstract class TradeFilterStoreBase with Store { displayXMRTO = false; displayMorphToken = false; displaySimpleSwap = false; + displayTrocador = false; } else { displayChangeNow = true; displaySideShift = true; displayXMRTO = true; displayMorphToken = true; displaySimpleSwap = true; + displayTrocador = true; } break; } @@ -88,7 +97,8 @@ abstract class TradeFilterStoreBase with Store { ExchangeProviderDescription.morphToken) ||(displaySimpleSwap && item.trade.provider == - ExchangeProviderDescription.simpleSwap)) + ExchangeProviderDescription.simpleSwap) + ||(displayTrocador && item.trade.provider == ExchangeProviderDescription.trocador)) .toList() : _trades; } diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart index b10e0d08d..989fdae33 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -31,7 +31,7 @@ abstract class SettingsStoreBase with Store { required bool initialSaveRecipientAddress, required FiatApiMode initialFiatMode, required bool initialAllowBiometricalAuthentication, - required bool initialExchangeEnabled, + required FiatApiMode initialExchangeStatus, required ThemeBase initialTheme, required int initialPinLength, required String initialLanguageCode, @@ -53,7 +53,7 @@ abstract class SettingsStoreBase with Store { shouldSaveRecipientAddress = initialSaveRecipientAddress, fiatApiMode = initialFiatMode, allowBiometricalAuthentication = initialAllowBiometricalAuthentication, - disableExchange = initialExchangeEnabled, + exchangeStatus = initialExchangeStatus, currentTheme = initialTheme, pinCodeLength = initialPinLength, languageCode = initialLanguageCode, @@ -153,9 +153,9 @@ abstract class SettingsStoreBase with Store { PreferencesKey.currentBalanceDisplayModeKey, mode.serialize())); reaction( - (_) => disableExchange, - (bool disableExchange) => sharedPreferences.setBool( - PreferencesKey.disableExchangeKey, disableExchange)); + (_) => exchangeStatus, + (FiatApiMode mode) => sharedPreferences.setInt( + PreferencesKey.exchangeStatusKey, mode.serialize())); this .nodes @@ -192,7 +192,7 @@ abstract class SettingsStoreBase with Store { bool allowBiometricalAuthentication; @observable - bool disableExchange; + FiatApiMode exchangeStatus; @observable ThemeBase currentTheme; @@ -284,8 +284,9 @@ abstract class SettingsStoreBase with Store { final allowBiometricalAuthentication = sharedPreferences .getBool(PreferencesKey.allowBiometricalAuthenticationKey) ?? false; - final disableExchange = sharedPreferences - .getBool(PreferencesKey.disableExchangeKey) ?? false; + final exchangeStatus = FiatApiMode.deserialize( + raw: sharedPreferences + .getInt(PreferencesKey.exchangeStatusKey) ?? FiatApiMode.enabled.raw); final legacyTheme = (sharedPreferences.getBool(PreferencesKey.isDarkThemeLegacy) ?? false) ? ThemeType.dark.index @@ -354,7 +355,7 @@ abstract class SettingsStoreBase with Store { initialSaveRecipientAddress: shouldSaveRecipientAddress, initialFiatMode: currentFiatApiMode, initialAllowBiometricalAuthentication: allowBiometricalAuthentication, - initialExchangeEnabled: disableExchange, + initialExchangeStatus: exchangeStatus, initialTheme: savedTheme, actionlistDisplayMode: actionListDisplayMode, initialPinLength: pinLength, @@ -400,7 +401,9 @@ abstract class SettingsStoreBase with Store { allowBiometricalAuthentication = sharedPreferences .getBool(PreferencesKey.allowBiometricalAuthenticationKey) ?? allowBiometricalAuthentication; - disableExchange = sharedPreferences.getBool(PreferencesKey.disableExchangeKey) ?? disableExchange; + exchangeStatus = FiatApiMode.deserialize( + raw: sharedPreferences + .getInt(PreferencesKey.exchangeStatusKey) ?? FiatApiMode.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 f800e3418..80b62fdd0 100644 --- a/lib/view_model/advanced_privacy_settings_view_model.dart +++ b/lib/view_model/advanced_privacy_settings_view_model.dart @@ -1,5 +1,7 @@ import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cake_wallet/store/settings_store.dart'; +import 'package:cake_wallet/view_model/settings/choices_list_item.dart'; +import 'package:cake_wallet/view_model/settings/settings_list_item.dart'; import 'package:cake_wallet/view_model/settings/switcher_list_item.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:mobx/mobx.dart'; @@ -14,18 +16,19 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store { AdvancedPrivacySettingsViewModelBase(this.type, this._settingsStore) : _addCustomNode = false { settings = [ - SwitcherListItem( - title: S.current.disable_fiat, - value: () => _settingsStore.fiatApiMode == FiatApiMode.disabled, - onValueChange: (_, bool value) => setFiatMode(value), - ), - SwitcherListItem( - title: S.current.disable_exchange, - value: () => _settingsStore.disableExchange, - onValueChange: (_, bool value) { - _settingsStore.disableExchange = value; - }, - ), + ChoicesListItem( + title: S.current.fiat_api, + items: FiatApiMode.all, + selectedItem: _settingsStore.fiatApiMode, + onItemSelected: (FiatApiMode mode) => setFiatMode(mode), + ), + + ChoicesListItem( + title: S.current.exchange, + items: FiatApiMode.all, + selectedItem: _settingsStore.exchangeStatus, + onItemSelected: (FiatApiMode mode) => _settingsStore.exchangeStatus = mode, + ), SwitcherListItem( title: S.current.add_custom_node, value: () => _addCustomNode, @@ -34,7 +37,7 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store { ]; } - late List settings; + late List settings; @observable bool _addCustomNode = false; @@ -46,11 +49,7 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store { bool get addCustomNode => _addCustomNode; @action - void setFiatMode(bool value) { - if (value) { - _settingsStore.fiatApiMode = FiatApiMode.disabled; - return; - } - _settingsStore.fiatApiMode = FiatApiMode.enabled; + void setFiatMode(FiatApiMode value) { + _settingsStore.fiatApiMode = value; } } diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index 57720d92f..dab2bafd1 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cake_wallet/wallet_type_utils.dart'; import 'package:cw_core/transaction_history.dart'; import 'package:cw_core/balance.dart'; @@ -96,6 +97,11 @@ abstract class DashboardViewModelBase with Store { caption: ExchangeProviderDescription.simpleSwap.title, onChanged: () => tradeFilterStore .toggleDisplayExchange(ExchangeProviderDescription.simpleSwap)), + FilterItem( + value: () => tradeFilterStore.displayTrocador, + caption: ExchangeProviderDescription.trocador.title, + onChanged: () => tradeFilterStore + .toggleDisplayExchange(ExchangeProviderDescription.trocador)), ] }, subname = '', @@ -268,7 +274,7 @@ abstract class DashboardViewModelBase with Store { settingsStore.shouldShowYatPopup = shouldShow; @computed - bool get isEnabledExchangeAction => !settingsStore.disableExchange; + bool get isEnabledExchangeAction => settingsStore.exchangeStatus != FiatApiMode.disabled; @observable bool hasExchangeAction; diff --git a/lib/view_model/exchange/exchange_trade_view_model.dart b/lib/view_model/exchange/exchange_trade_view_model.dart index 541b74396..94c874979 100644 --- a/lib/view_model/exchange/exchange_trade_view_model.dart +++ b/lib/view_model/exchange/exchange_trade_view_model.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:cake_wallet/exchange/sideshift/sideshift_exchange_provider.dart'; import 'package:cake_wallet/exchange/simpleswap/simpleswap_exchange_provider.dart'; +import 'package:cake_wallet/exchange/trocador/trocador_exchange_provider.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cake_wallet/exchange/changenow/changenow_exchange_provider.dart'; @@ -46,6 +47,9 @@ abstract class ExchangeTradeViewModelBase with Store { case ExchangeProviderDescription.simpleSwap: _provider = SimpleSwapExchangeProvider(); break; + case ExchangeProviderDescription.trocador: + _provider = TrocadorExchangeProvider(); + break; } _updateItems(); diff --git a/lib/view_model/exchange/exchange_view_model.dart b/lib/view_model/exchange/exchange_view_model.dart index 5c1f696b8..f9f5683d9 100644 --- a/lib/view_model/exchange/exchange_view_model.dart +++ b/lib/view_model/exchange/exchange_view_model.dart @@ -7,6 +7,8 @@ import 'package:cake_wallet/exchange/sideshift/sideshift_exchange_provider.dart' import 'package:cake_wallet/exchange/sideshift/sideshift_request.dart'; import 'package:cake_wallet/exchange/simpleswap/simpleswap_exchange_provider.dart'; import 'package:cake_wallet/exchange/simpleswap/simpleswap_request.dart'; +import 'package:cake_wallet/exchange/trocador/trocador_exchange_provider.dart'; +import 'package:cake_wallet/exchange/trocador/trocador_request.dart'; import 'package:cw_core/transaction_priority.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/crypto_currency.dart'; @@ -60,7 +62,7 @@ abstract class ExchangeViewModelBase with Store { limitsState = LimitsInitialState(), receiveCurrency = wallet.currency, depositCurrency = wallet.currency, - providerList = [ChangeNowExchangeProvider(), SideShiftExchangeProvider(), SimpleSwapExchangeProvider()], + providerList = [ChangeNowExchangeProvider(), SideShiftExchangeProvider(), SimpleSwapExchangeProvider(), TrocadorExchangeProvider()], selectedProviders = ObservableList() { const excludeDepositCurrencies = [CryptoCurrency.btt, CryptoCurrency.nano]; const excludeReceiveCurrencies = [CryptoCurrency.xlm, CryptoCurrency.xrp, @@ -449,6 +451,18 @@ abstract class ExchangeViewModelBase with Store { amount = isFixedRateMode ? receiveAmount : depositAmount; } + if (provider is TrocadorExchangeProvider) { + request = TrocadorRequest( + from: depositCurrency, + to: receiveCurrency, + fromAmount: depositAmount.replaceAll(',', '.'), + toAmount: receiveAmount.replaceAll(',', '.'), + refundAddress: depositAddress, + address: receiveAddress, + isReverse: isFixedRateMode); + amount = isFixedRateMode ? receiveAmount : depositAmount; + } + amount = amount.replaceAll(',', '.'); if (limitsState is LimitsLoadedSuccessfully) { diff --git a/lib/view_model/settings/privacy_settings_view_model.dart b/lib/view_model/settings/privacy_settings_view_model.dart index f8c3e5b50..db345d0f2 100644 --- a/lib/view_model/settings/privacy_settings_view_model.dart +++ b/lib/view_model/settings/privacy_settings_view_model.dart @@ -12,27 +12,23 @@ abstract class PrivacySettingsViewModelBase with Store { final SettingsStore _settingsStore; @computed - bool get disableExchange => _settingsStore.disableExchange; + FiatApiMode get exchangeStatus => _settingsStore.exchangeStatus; @computed bool get shouldSaveRecipientAddress => _settingsStore.shouldSaveRecipientAddress; @computed - bool get isFiatDisabled => _settingsStore.fiatApiMode == FiatApiMode.disabled; + FiatApiMode get fiatApi => _settingsStore.fiatApiMode; @action void setShouldSaveRecipientAddress(bool value) => _settingsStore.shouldSaveRecipientAddress = value; @action - void setEnableExchange(bool value) => _settingsStore.disableExchange = value; + void setEnableExchange(FiatApiMode value) => _settingsStore.exchangeStatus = value; @action - void setFiatMode(bool value) { - if (value) { - _settingsStore.fiatApiMode = FiatApiMode.disabled; - return; - } - _settingsStore.fiatApiMode = FiatApiMode.enabled; + void setFiatMode(FiatApiMode value) { + _settingsStore.fiatApiMode = value; } } diff --git a/lib/view_model/trade_details_view_model.dart b/lib/view_model/trade_details_view_model.dart index 5a1f78774..97bb40fe4 100644 --- a/lib/view_model/trade_details_view_model.dart +++ b/lib/view_model/trade_details_view_model.dart @@ -6,6 +6,7 @@ import 'package:cake_wallet/exchange/morphtoken/morphtoken_exchange_provider.dar import 'package:cake_wallet/exchange/sideshift/sideshift_exchange_provider.dart'; import 'package:cake_wallet/exchange/simpleswap/simpleswap_exchange_provider.dart'; import 'package:cake_wallet/exchange/trade.dart'; +import 'package:cake_wallet/exchange/trocador/trocador_exchange_provider.dart'; import 'package:cake_wallet/exchange/xmrto/xmrto_exchange_provider.dart'; import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/utils/date_formatter.dart'; @@ -48,6 +49,9 @@ abstract class TradeDetailsViewModelBase with Store { case ExchangeProviderDescription.simpleSwap: _provider = SimpleSwapExchangeProvider(); break; + case ExchangeProviderDescription.trocador: + _provider = TrocadorExchangeProvider(); + break; } items = ObservableList(); diff --git a/tool/utils/secret_key.dart b/tool/utils/secret_key.dart index e5202a829..ff74da8a7 100644 --- a/tool/utils/secret_key.dart +++ b/tool/utils/secret_key.dart @@ -29,6 +29,7 @@ class SecretKey { SecretKey('anypayToken', () => ''), SecretKey('onramperApiKey', () => ''), SecretKey('ioniaClientId', () => ''), + SecretKey('trocadorApiKey', () => ''), ]; final String name; From 37045578c27f3edc368fd4a5f5b71f148af62815 Mon Sep 17 00:00:00 2001 From: Godwin Asuquo Date: Mon, 6 Feb 2023 21:42:17 +0200 Subject: [PATCH 02/21] Check if fixed rate --- lib/exchange/trocador/trocador_exchange_provider.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/exchange/trocador/trocador_exchange_provider.dart b/lib/exchange/trocador/trocador_exchange_provider.dart index 18ea27895..6babc8e94 100644 --- a/lib/exchange/trocador/trocador_exchange_provider.dart +++ b/lib/exchange/trocador/trocador_exchange_provider.dart @@ -160,8 +160,8 @@ class TrocadorExchangeProvider extends ExchangeProvider { 'ticker_to': to.title.toLowerCase(), 'network_from': _networkFor(from), 'network_to': _networkFor(to), - 'amount_from': amount.toString(), - 'amount_to': amount.toString(), + if (!isFixedRateMode) 'amount_from': amount.toString(), + if (isFixedRateMode) 'amount_to': amount.toString(), 'payment': isFixedRateMode ? 'True' : 'False', 'min_kycrating': 'C', 'markup': '3', From a5179939615ddbb2314090abda4d2b466daa19de Mon Sep 17 00:00:00 2001 From: Godwin Asuquo Date: Mon, 6 Feb 2023 22:20:46 +0200 Subject: [PATCH 03/21] add trocador key to secrets --- .github/workflows/pr_test_build.yml | 2 ++ lib/exchange/trocador/trocador_exchange_provider.dart | 5 +++-- tool/utils/secret_key.dart | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index c3c61865f..1766bbe86 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -111,6 +111,8 @@ jobs: echo "const anypayToken = '${{ secrets.ANY_PAY_TOKEN }}';" >> lib/.secrets.g.dart echo "const ioniaClientId = '${{ secrets.IONIA_CLIENT_ID }}';" >> lib/.secrets.g.dart echo "const twitterBearerToken = '${{ secrets.TWITTER_BEARER_TOKEN }}';" >> lib/.secrets.g.dart + echo "const trocadorApiKey = '${{ secrets.TROCADOR_API_KEY }}';" >> lib/.secrets.g.dart + echo "const trocadorExchangeMarkup = '${{ secrets.TROCADOR_EXCHANGE_MARKUP }}';" >> lib/.secrets.g.dart - name: Rename app run: sed -i -e "s/\${APP_NAME}/$GITHUB_HEAD_REF/g" /opt/android/cake_wallet/android/app/src/main/AndroidManifest.xml diff --git a/lib/exchange/trocador/trocador_exchange_provider.dart b/lib/exchange/trocador/trocador_exchange_provider.dart index 6babc8e94..9785b67c2 100644 --- a/lib/exchange/trocador/trocador_exchange_provider.dart +++ b/lib/exchange/trocador/trocador_exchange_provider.dart @@ -43,6 +43,7 @@ class TrocadorExchangeProvider extends ExchangeProvider { static const onionApiAuthority = 'trocadorfyhlu27aefre5u7zri66gudtzdyelymftvr4yjwcxhfaqsid.onion'; static const clearNetAuthority = 'trocador.app'; static const apiKey = secrets.trocadorApiKey; + static const markup = secrets.trocadorExchangeMarkup; static const newRatePath = '/api/new_rate'; static const createTradePath = 'api/new_trade'; static const tradePath = 'api/trade'; @@ -69,7 +70,7 @@ class TrocadorExchangeProvider extends ExchangeProvider { 'network_to': _networkFor(request.to), 'payment': isFixedRateMode ? 'True' : 'False', 'min_kycrating': 'C', - 'markup': '3', + 'markup': markup, 'best_only': 'True', if (!isFixedRateMode) 'amount_from': request.fromAmount, if (isFixedRateMode) 'amount_to': request.toAmount, @@ -164,7 +165,7 @@ class TrocadorExchangeProvider extends ExchangeProvider { if (isFixedRateMode) 'amount_to': amount.toString(), 'payment': isFixedRateMode ? 'True' : 'False', 'min_kycrating': 'C', - 'markup': '3', + 'markup': markup, 'best_only': 'True', }; diff --git a/tool/utils/secret_key.dart b/tool/utils/secret_key.dart index 8ac236088..68308d1c6 100644 --- a/tool/utils/secret_key.dart +++ b/tool/utils/secret_key.dart @@ -25,6 +25,7 @@ class SecretKey { SecretKey('onramperApiKey', () => ''), SecretKey('ioniaClientId', () => ''), SecretKey('trocadorApiKey', () => ''), + SecretKey('trocadorExchangeMarkup', () => '3'), SecretKey('twitterBearerToken', () => ''), ]; From bf73fb24e575faaedadea451ac86dd9a73d8acf9 Mon Sep 17 00:00:00 2001 From: Godwin Asuquo Date: Mon, 6 Feb 2023 23:18:54 +0200 Subject: [PATCH 04/21] [skip ci] remove default value for trocador --- tool/utils/secret_key.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tool/utils/secret_key.dart b/tool/utils/secret_key.dart index 68308d1c6..c27bc5ff7 100644 --- a/tool/utils/secret_key.dart +++ b/tool/utils/secret_key.dart @@ -25,7 +25,7 @@ class SecretKey { SecretKey('onramperApiKey', () => ''), SecretKey('ioniaClientId', () => ''), SecretKey('trocadorApiKey', () => ''), - SecretKey('trocadorExchangeMarkup', () => '3'), + SecretKey('trocadorExchangeMarkup', () => ''), SecretKey('twitterBearerToken', () => ''), ]; From f770e095a1b765984aa4e8bff5ccb04592b0cc9a Mon Sep 17 00:00:00 2001 From: Godwin Asuquo Date: Tue, 7 Feb 2023 17:12:34 +0200 Subject: [PATCH 05/21] Fix advance privacy setting set state --- .../advanced_privacy_settings_page.dart | 66 ++++++++++--------- .../advanced_privacy_settings_view_model.dart | 47 +++++-------- 2 files changed, 53 insertions(+), 60 deletions(-) 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 2a9f4d291..7d720e4e5 100644 --- a/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart +++ b/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart @@ -5,7 +5,6 @@ import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell. import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart'; import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart'; import 'package:cake_wallet/view_model/settings/choices_list_item.dart'; -import 'package:cake_wallet/view_model/settings/switcher_list_item.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/generated/i18n.dart'; @@ -49,38 +48,43 @@ class _AdvancedPrivacySettingsBodyState extends State>().map( - (item) => Observer( - builder: (_) => SettingsChoicesCell(item) - ), - ), - ...widget.privacySettingsViewModel.settings.whereType().map( - (item) => Observer( - builder: (_) => SettingsSwitcherCell( - title: item.title, - value: item.value(), - onValueChange: item.onValueChange, + content: Observer( + builder: (_) => 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), ), ), - ), - Observer( - builder: (_) { - if (widget.privacySettingsViewModel.addCustomNode) { - return Padding( - padding: EdgeInsets.only(left: 24, right: 24, top: 24), - child: NodeForm( - formKey: _formKey, - nodeViewModel: widget.nodeViewModel, - ), - ); - } - return const SizedBox(); - }, - ), - ], + 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, + ), + ) + ], + ), ), bottomSectionPadding: EdgeInsets.all(24), bottomSection: Column( diff --git a/lib/view_model/advanced_privacy_settings_view_model.dart b/lib/view_model/advanced_privacy_settings_view_model.dart index 80b62fdd0..7d526047d 100644 --- a/lib/view_model/advanced_privacy_settings_view_model.dart +++ b/lib/view_model/advanced_privacy_settings_view_model.dart @@ -1,11 +1,7 @@ import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cake_wallet/store/settings_store.dart'; -import 'package:cake_wallet/view_model/settings/choices_list_item.dart'; -import 'package:cake_wallet/view_model/settings/settings_list_item.dart'; -import 'package:cake_wallet/view_model/settings/switcher_list_item.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:mobx/mobx.dart'; -import 'package:cake_wallet/generated/i18n.dart'; part 'advanced_privacy_settings_view_model.g.dart'; @@ -13,36 +9,19 @@ class AdvancedPrivacySettingsViewModel = AdvancedPrivacySettingsViewModelBase with _$AdvancedPrivacySettingsViewModel; abstract class AdvancedPrivacySettingsViewModelBase with Store { - AdvancedPrivacySettingsViewModelBase(this.type, this._settingsStore) - : _addCustomNode = false { - settings = [ - ChoicesListItem( - title: S.current.fiat_api, - items: FiatApiMode.all, - selectedItem: _settingsStore.fiatApiMode, - onItemSelected: (FiatApiMode mode) => setFiatMode(mode), - ), - - ChoicesListItem( - title: S.current.exchange, - items: FiatApiMode.all, - selectedItem: _settingsStore.exchangeStatus, - onItemSelected: (FiatApiMode mode) => _settingsStore.exchangeStatus = mode, - ), - SwitcherListItem( - title: S.current.add_custom_node, - value: () => _addCustomNode, - onValueChange: (_, bool value) => _addCustomNode = value, - ), - ]; - } + AdvancedPrivacySettingsViewModelBase(this.type, this._settingsStore) : _addCustomNode = false; - late List settings; + @computed + FiatApiMode get exchangeStatus => _settingsStore.exchangeStatus; + + @computed + FiatApiMode get fiatApi => _settingsStore.fiatApiMode; @observable bool _addCustomNode = false; final WalletType type; + final SettingsStore _settingsStore; @computed @@ -50,6 +29,16 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store { @action void setFiatMode(FiatApiMode value) { - _settingsStore.fiatApiMode = value; + _settingsStore.fiatApiMode = value; + } + + @action + void setEnableExchange(FiatApiMode value) { + _settingsStore.exchangeStatus = value; + } + + @action + void toggleAddCustomNode() { + _addCustomNode = !_addCustomNode; } } From f1b0a5adb6369b66d2e104083f5a1bfe4dd1485c Mon Sep 17 00:00:00 2001 From: Godwin Asuquo Date: Tue, 7 Feb 2023 19:39:39 +0200 Subject: [PATCH 06/21] Add trocador provider and password to transaction details --- lib/exchange/trade.dart | 51 +++++++++++-------- .../trocador/trocador_exchange_provider.dart | 12 +++++ lib/view_model/trade_details_view_model.dart | 47 ++++++++--------- 3 files changed, 67 insertions(+), 43 deletions(-) diff --git a/lib/exchange/trade.dart b/lib/exchange/trade.dart index f20a080b7..70dfa5713 100644 --- a/lib/exchange/trade.dart +++ b/lib/exchange/trade.dart @@ -8,21 +8,25 @@ part 'trade.g.dart'; @HiveType(typeId: Trade.typeId) class Trade extends HiveObject { - Trade( - {required this.id, - required this.amount, - ExchangeProviderDescription? provider, - CryptoCurrency? from, - CryptoCurrency? to, - TradeState? state, - this.createdAt, - this.expiredAt, - this.inputAddress, - this.extraId, - this.outputTransaction, - this.refundAddress, - this.walletId, - this.payoutAddress}) { + Trade({ + required this.id, + required this.amount, + ExchangeProviderDescription? provider, + CryptoCurrency? from, + CryptoCurrency? to, + TradeState? state, + this.createdAt, + this.expiredAt, + this.inputAddress, + this.extraId, + this.outputTransaction, + this.refundAddress, + this.walletId, + this.payoutAddress, + this.password, + this.providerId, + this.providerName, + }) { if (provider != null) { providerRaw = provider.raw; } @@ -92,16 +96,23 @@ class Trade extends HiveObject { @HiveField(13) String? payoutAddress; + @HiveField(14) + String? password; + + @HiveField(15) + String? providerId; + + @HiveField(16) + String? providerName; + static Trade fromMap(Map map) { return Trade( id: map['id'] as String, - provider: ExchangeProviderDescription.deserialize( - raw: map['provider'] as int), + provider: ExchangeProviderDescription.deserialize(raw: map['provider'] as int), from: CryptoCurrency.deserialize(raw: map['input'] as int), to: CryptoCurrency.deserialize(raw: map['output'] as int), - createdAt: map['date'] != null - ? DateTime.fromMillisecondsSinceEpoch(map['date'] as int) - : null, + createdAt: + map['date'] != null ? DateTime.fromMillisecondsSinceEpoch(map['date'] as int) : null, amount: map['amount'] as String, walletId: map['wallet_id'] as String); } diff --git a/lib/exchange/trocador/trocador_exchange_provider.dart b/lib/exchange/trocador/trocador_exchange_provider.dart index 9785b67c2..ecdeebe84 100644 --- a/lib/exchange/trocador/trocador_exchange_provider.dart +++ b/lib/exchange/trocador/trocador_exchange_provider.dart @@ -113,6 +113,9 @@ class TrocadorExchangeProvider extends ExchangeProvider { final state = TradeState.deserialize(raw: status); final payoutAddress = responseJSON['address_user'] as String; final date = responseJSON['date'] as String; + final password = responseJSON['password'] as String; + final providerId = responseJSON['id_provider'] as String; + final providerName = responseJSON['provider'] as String; return Trade( id: id, @@ -122,6 +125,9 @@ class TrocadorExchangeProvider extends ExchangeProvider { inputAddress: inputAddress, refundAddress: refundAddress, state: state, + password: password, + providerId: providerId, + providerName: providerName, createdAt: DateTime.tryParse(date)?.toLocal(), amount: responseJSON['amount_from']?.toString() ?? request.fromAmount, payoutAddress: payoutAddress); @@ -208,6 +214,9 @@ class TrocadorExchangeProvider extends ExchangeProvider { final to = CryptoCurrency.fromString(responseJSON['ticker_to'] as String); final state = TradeState.deserialize(raw: responseJSON['status'] as String); final date = DateTime.parse(responseJSON['date'] as String); + final password = responseJSON['password'] as String; + final providerId = responseJSON['id_provider'] as String; + final providerName = responseJSON['provider'] as String; return Trade( id: id, @@ -220,6 +229,9 @@ class TrocadorExchangeProvider extends ExchangeProvider { amount: fromAmount, state: state, payoutAddress: payoutAddress, + password: password, + providerId: providerId, + providerName: providerName, ); }); } diff --git a/lib/view_model/trade_details_view_model.dart b/lib/view_model/trade_details_view_model.dart index 97bb40fe4..3a6f1ec74 100644 --- a/lib/view_model/trade_details_view_model.dart +++ b/lib/view_model/trade_details_view_model.dart @@ -23,16 +23,15 @@ import 'package:cake_wallet/src/screens/trade_details/trade_details_status_item. import 'package:url_launcher/url_launcher.dart'; part 'trade_details_view_model.g.dart'; -class TradeDetailsViewModel = TradeDetailsViewModelBase - with _$TradeDetailsViewModel; +class TradeDetailsViewModel = TradeDetailsViewModelBase with _$TradeDetailsViewModel; abstract class TradeDetailsViewModelBase with Store { TradeDetailsViewModelBase({ required Trade tradeForDetails, required this.trades, - required this.settingsStore}) - : items = ObservableList(), - trade = tradeForDetails { + required this.settingsStore, + }) : items = ObservableList(), + trade = tradeForDetails { switch (trade.provider) { case ExchangeProviderDescription.xmrto: _provider = XMRTOExchangeProvider(); @@ -46,7 +45,7 @@ abstract class TradeDetailsViewModelBase with Store { case ExchangeProviderDescription.sideShift: _provider = SideShiftExchangeProvider(); break; - case ExchangeProviderDescription.simpleSwap: + case ExchangeProviderDescription.simpleSwap: _provider = SimpleSwapExchangeProvider(); break; case ExchangeProviderDescription.trocador: @@ -100,12 +99,7 @@ abstract class TradeDetailsViewModelBase with Store { items.clear(); items.add( - DetailsListStatusItem( - title: S.current.trade_details_state, - value: trade.state != null - ? trade.state.toString() - : S.current.trade_details_fetching) - ); + DetailsListStatusItem(title: S.current.trade_details_state, value: trade.state.toString())); items.add(TradeDetailsListCardItem.tradeDetails( id: trade.id, @@ -118,15 +112,11 @@ abstract class TradeDetailsViewModelBase with Store { }, )); - if (trade.provider != null) { - items.add(StandartListItem( - title: S.current.trade_details_provider, - value: trade.provider.toString())); - } + items.add(StandartListItem( + title: S.current.trade_details_provider, value: trade.provider.toString())); if (trade.provider == ExchangeProviderDescription.changeNow) { - final buildURL = - 'https://changenow.io/exchange/txs/${trade.id.toString()}'; + final buildURL = 'https://changenow.io/exchange/txs/${trade.id.toString()}'; items.add(TrackTradeListItem( title: 'Track', value: buildURL, @@ -137,14 +127,25 @@ abstract class TradeDetailsViewModelBase with Store { if (trade.provider == ExchangeProviderDescription.sideShift) { final buildURL = 'https://sideshift.ai/orders/${trade.id.toString()}'; - items.add(TrackTradeListItem( - title: 'Track', value: buildURL, onTap: () => launch(buildURL))); + items.add(TrackTradeListItem(title: 'Track', value: buildURL, onTap: () => launch(buildURL))); } if (trade.provider == ExchangeProviderDescription.simpleSwap) { final buildURL = 'https://simpleswap.io/exchange?id=${trade.id.toString()}'; - items.add(TrackTradeListItem( - title: 'Track', value: buildURL, onTap: () => launch(buildURL))); + items.add(TrackTradeListItem(title: 'Track', value: buildURL, onTap: () => launch(buildURL))); + } + + if (trade.provider == ExchangeProviderDescription.trocador) { + final buildURL = 'https://trocador.app/en/checkout/${trade.id.toString()}'; + items.add(TrackTradeListItem(title: 'Track', value: buildURL, onTap: () => launch(buildURL))); + + items.add(StandartListItem( + title: '${trade.providerName} ${S.current.id.toUpperCase()}', + value: trade.providerId ?? '')); + + if (trade.password != null && trade.password!.isNotEmpty) + items.add(StandartListItem( + title: '${trade.providerName} ${S.current.password}', value: trade.password ?? '')); } } } From 5f6b4477064cd88eda09d8ecc8e8a4478b9b2072 Mon Sep 17 00:00:00 2001 From: Godwin Asuquo Date: Thu, 9 Feb 2023 18:24:59 +0200 Subject: [PATCH 07/21] Fix _network normalize function --- lib/exchange/trocador/trocador_exchange_provider.dart | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/exchange/trocador/trocador_exchange_provider.dart b/lib/exchange/trocador/trocador_exchange_provider.dart index ecdeebe84..cfbc5001a 100644 --- a/lib/exchange/trocador/trocador_exchange_provider.dart +++ b/lib/exchange/trocador/trocador_exchange_provider.dart @@ -252,12 +252,7 @@ class TrocadorExchangeProvider extends ExchangeProvider { String get title => 'Trocador'; String _networkFor(CryptoCurrency currency) { - switch (currency) { - case CryptoCurrency.usdt: - return CryptoCurrency.btc.title.toLowerCase(); - default: - return currency.tag != null ? _normalizeTag(currency.tag!) : 'Mainnet'; - } + return currency.tag != null ? _normalizeTag(currency.tag!) : 'Mainnet'; } String _normalizeTag(String tag) { From 6711d9530f7ed89dd9e767b948470f5b2b620c72 Mon Sep 17 00:00:00 2001 From: Justin Ehrenhofer Date: Thu, 9 Feb 2023 10:52:24 -0600 Subject: [PATCH 08/21] Normalize remaining tags/networks Also rename Cake `C-CHAIN` tag to `AVAXC`, which will result in a clearer situation for user if we later add USDT (AVAXC), for example. --- cw_core/lib/crypto_currency.dart | 2 +- .../trocador/trocador_exchange_provider.dart | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/cw_core/lib/crypto_currency.dart b/cw_core/lib/crypto_currency.dart index 90051f1f7..6c4bec03a 100644 --- a/cw_core/lib/crypto_currency.dart +++ b/cw_core/lib/crypto_currency.dart @@ -117,7 +117,7 @@ class CryptoCurrency extends EnumerableItem with Serializable { static const xusd = CryptoCurrency(title: 'XUSD', tag: 'XHV', raw: 29, name: 'xusd'); static const ape = CryptoCurrency(title: 'APE', tag: 'ETH', fullName: 'ApeCoin', raw: 30, name: 'ape', iconPath: 'assets/images/ape_icon.png'); - static const avaxc = CryptoCurrency(title: 'AVAX', tag: 'C-CHAIN', raw: 31, name: 'avaxc', iconPath: 'assets/images/avaxc_icon.png'); + static const avaxc = CryptoCurrency(title: 'AVAX', tag: 'AVAXC', raw: 31, name: 'avaxc', iconPath: 'assets/images/avaxc_icon.png'); static const btt = CryptoCurrency(title: 'BTT', tag: 'ETH', fullName: 'BitTorrent', raw: 32, name: 'btt', iconPath: 'assets/images/btt_icon.png'); static const bttc = CryptoCurrency(title: 'BTTC', tag: 'TRX', fullName: 'BitTorrent-NEW', raw: 33, name: 'bttc', iconPath: 'assets/images/bttbsc_icon.png'); static const doge = CryptoCurrency(title: 'DOGE', fullName: 'Dogecoin', raw: 34, name: 'doge', iconPath: 'assets/images/doge_icon.png'); diff --git a/lib/exchange/trocador/trocador_exchange_provider.dart b/lib/exchange/trocador/trocador_exchange_provider.dart index cfbc5001a..3bf52040a 100644 --- a/lib/exchange/trocador/trocador_exchange_provider.dart +++ b/lib/exchange/trocador/trocador_exchange_provider.dart @@ -26,8 +26,6 @@ class TrocadorExchangeProvider extends ExchangeProvider { CryptoCurrency.stx, CryptoCurrency.bttc, CryptoCurrency.zaddr, - CryptoCurrency.usdcpoly, - CryptoCurrency.maticpoly, ]; static List _supportedPairs() { @@ -252,7 +250,18 @@ class TrocadorExchangeProvider extends ExchangeProvider { String get title => 'Trocador'; String _networkFor(CryptoCurrency currency) { - return currency.tag != null ? _normalizeTag(currency.tag!) : 'Mainnet'; + switch (currency) { + case CryptoCurrency.eth: + return 'ERC20'; + case CryptoCurrency.maticpoly: + return 'Mainnet'; + case CryptoCurrency.usdcpoly: + return 'MATIC'; + case CryptoCurrency.zec: + return 'Mainnet'; + default: + return currency.tag != null ? _normalizeTag(currency.tag!) : 'Mainnet'; + } } String _normalizeTag(String tag) { From 891a9d3368189ca710c952c2a4a6d757659a0c6f Mon Sep 17 00:00:00 2001 From: Justin Ehrenhofer Date: Mon, 13 Feb 2023 08:51:26 -0600 Subject: [PATCH 09/21] Add 5 new assets and map TRX -> TRC20 --- lib/exchange/trocador/trocador_exchange_provider.dart | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/exchange/trocador/trocador_exchange_provider.dart b/lib/exchange/trocador/trocador_exchange_provider.dart index 3bf52040a..457757ea7 100644 --- a/lib/exchange/trocador/trocador_exchange_provider.dart +++ b/lib/exchange/trocador/trocador_exchange_provider.dart @@ -18,13 +18,8 @@ class TrocadorExchangeProvider extends ExchangeProvider { super(pairList: _supportedPairs()); static const List _notSupported = [ - CryptoCurrency.xhv, - CryptoCurrency.dcr, - CryptoCurrency.oxt, - CryptoCurrency.pivx, CryptoCurrency.scrt, CryptoCurrency.stx, - CryptoCurrency.bttc, CryptoCurrency.zaddr, ]; @@ -268,6 +263,8 @@ class TrocadorExchangeProvider extends ExchangeProvider { switch (tag) { case 'ETH': return 'ERC20'; + case 'TRX': + return 'TRC20'; default: return tag.toLowerCase(); } From 8419767c4f39dc33cd3ac974322bb826d3e0ddd1 Mon Sep 17 00:00:00 2001 From: Godwin Asuquo Date: Tue, 28 Feb 2023 18:12:24 +0200 Subject: [PATCH 10/21] Add limit query to trocador --- .../trocador/trocador_exchange_provider.dart | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/lib/exchange/trocador/trocador_exchange_provider.dart b/lib/exchange/trocador/trocador_exchange_provider.dart index 457757ea7..49503a1d0 100644 --- a/lib/exchange/trocador/trocador_exchange_provider.dart +++ b/lib/exchange/trocador/trocador_exchange_provider.dart @@ -40,6 +40,7 @@ class TrocadorExchangeProvider extends ExchangeProvider { static const newRatePath = '/api/new_rate'; static const createTradePath = 'api/new_trade'; static const tradePath = 'api/trade'; + static const coinPath = 'api/coin'; String _lastUsedRateId; @override @@ -134,9 +135,34 @@ class TrocadorExchangeProvider extends ExchangeProvider { {required CryptoCurrency from, required CryptoCurrency to, required bool isFixedRateMode}) async { - //TODO: implement limits from trocador api + + final params = { + 'api_key': apiKey, + 'ticker': from.title.toLowerCase(), + 'name': from.name, + }; + + final String apiAuthority = shouldUseOnionAddress ? await _getAuthority() : clearNetAuthority; + final uri = Uri.https(apiAuthority, coinPath, params); + + final response = await get(uri); + + if (response.statusCode != 200) { + throw Exception('Unexpected http status: ${response.statusCode}'); + } + + final responseJSON = json.decode(response.body) as List; + + if (responseJSON.isEmpty) { + throw Exception('No data'); + } + + final coinJson = responseJSON.first as Map; + + return Limits( - min: 0.0, + min: coinJson['minimum'] as double, + max: coinJson['maximum'] as double, ); } From 2c5b44426fcb32196af2354e6d6821d0f74226b9 Mon Sep 17 00:00:00 2001 From: Godwin Asuquo Date: Tue, 28 Feb 2023 20:34:51 +0200 Subject: [PATCH 11/21] Upgrade flutter packages --- .github/workflows/pr_test_build.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index 1766bbe86..a6a66cac2 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -77,6 +77,11 @@ 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 + flutter pub upgrade - name: Build generated code run: | From 2c9f17e728df856c790e0b4e44dc77a069746b08 Mon Sep 17 00:00:00 2001 From: Godwin Asuquo Date: Tue, 28 Feb 2023 20:45:03 +0200 Subject: [PATCH 12/21] Upgrade flutter packages --- .github/workflows/pr_test_build.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index a6a66cac2..60dd1bc68 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -81,7 +81,10 @@ jobs: - name: Upgrade flutter packages run: | cd /opt/android/cake_wallet - flutter pub upgrade + 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: | From dbfbadffdaef761abad0b493b27714c51fb22b01 Mon Sep 17 00:00:00 2001 From: Godwin Asuquo Date: Wed, 1 Mar 2023 15:50:31 +0200 Subject: [PATCH 13/21] Filter to use Tor only exchange --- lib/src/widgets/check_box_picker.dart | 16 ++++++++-------- .../exchange/exchange_view_model.dart | 19 ++++++++++++++++++- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/lib/src/widgets/check_box_picker.dart b/lib/src/widgets/check_box_picker.dart index e2f817fc4..80461e26d 100644 --- a/lib/src/widgets/check_box_picker.dart +++ b/lib/src/widgets/check_box_picker.dart @@ -38,7 +38,7 @@ class CheckBoxPickerState extends State { Column( mainAxisSize: MainAxisSize.min, children: [ - if (widget.title?.isNotEmpty ?? false) + if (widget.title.isNotEmpty) Container( padding: EdgeInsets.symmetric(horizontal: 24), child: Text( @@ -58,7 +58,7 @@ class CheckBoxPickerState extends State { child: ClipRRect( borderRadius: BorderRadius.all(Radius.circular(30)), child: Container( - color: Theme.of(context).accentTextTheme!.headline6!.color!, + color: Theme.of(context).accentTextTheme.headline6!.color!, child: ConstrainedBox( constraints: BoxConstraints( maxHeight: MediaQuery.of(context).size.height * 0.65, @@ -70,7 +70,7 @@ class CheckBoxPickerState extends State { child: Stack( alignment: Alignment.center, children: [ - (items?.length ?? 0) > 3 + (items.length) > 3 ? Scrollbar( controller: controller, child: itemsList(), @@ -95,14 +95,14 @@ class CheckBoxPickerState extends State { Widget itemsList() { return Container( - color: Theme.of(context).accentTextTheme!.headline6!.backgroundColor!, + color: Theme.of(context).accentTextTheme.headline6!.backgroundColor!, child: ListView.separated( padding: EdgeInsets.zero, controller: controller, shrinkWrap: true, separatorBuilder: (context, index) => widget.isSeparated ? Divider( - color: Theme.of(context).accentTextTheme!.headline6!.backgroundColor!, + color: Theme.of(context).accentTextTheme.headline6!.backgroundColor!, height: 1, ) : const SizedBox(), @@ -121,13 +121,13 @@ class CheckBoxPickerState extends State { }, child: Container( height: 55, - color: Theme.of(context).accentTextTheme!.headline6!.color!, + color: Theme.of(context).accentTextTheme.headline6!.color!, padding: EdgeInsets.only(left: 24, right: 24), child: CheckboxListTile( value: item.value, activeColor: item.value ? Palette.blueCraiola - : Theme.of(context).accentTextTheme!.subtitle1!.decorationColor!, + : Theme.of(context).accentTextTheme.subtitle1!.decorationColor!, checkColor: Colors.white, title: widget.displayItem?.call(item) ?? Text( @@ -138,7 +138,7 @@ class CheckBoxPickerState extends State { fontWeight: FontWeight.w600, color: item.isDisabled ? Colors.grey.withOpacity(0.5) - : Theme.of(context).primaryTextTheme!.headline6!.color!, + : Theme.of(context).primaryTextTheme.headline6!.color!, decoration: TextDecoration.none, ), ), diff --git a/lib/view_model/exchange/exchange_view_model.dart b/lib/view_model/exchange/exchange_view_model.dart index f9f5683d9..7921115c6 100644 --- a/lib/view_model/exchange/exchange_view_model.dart +++ b/lib/view_model/exchange/exchange_view_model.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:collection'; import 'dart:convert'; +import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/exchange/sideshift/sideshift_exchange_provider.dart'; import 'package:cake_wallet/exchange/sideshift/sideshift_request.dart'; @@ -62,8 +63,9 @@ abstract class ExchangeViewModelBase with Store { limitsState = LimitsInitialState(), receiveCurrency = wallet.currency, depositCurrency = wallet.currency, - providerList = [ChangeNowExchangeProvider(), SideShiftExchangeProvider(), SimpleSwapExchangeProvider(), TrocadorExchangeProvider()], + providerList = [], selectedProviders = ObservableList() { + _setProviders(); const excludeDepositCurrencies = [CryptoCurrency.btt, CryptoCurrency.nano]; const excludeReceiveCurrencies = [CryptoCurrency.xlm, CryptoCurrency.xrp, CryptoCurrency.bnb, CryptoCurrency.btt, CryptoCurrency.nano]; @@ -126,6 +128,13 @@ abstract class ExchangeViewModelBase with Store { final TradesStore tradesStore; final SharedPreferences sharedPreferences; + final _allProviders = [ + ChangeNowExchangeProvider(), + SideShiftExchangeProvider(), + SimpleSwapExchangeProvider(), + TrocadorExchangeProvider(), + ]; + @observable ExchangeProvider? provider; @@ -683,4 +692,12 @@ abstract class ExchangeViewModelBase with Store { break; } } + + void _setProviders(){ + if (_settingsStore.exchangeStatus == FiatApiMode.torOnly) { + providerList = _allProviders.where((provider) => provider.shouldUseOnionAddress).toList(); + } else { + providerList = _allProviders; + } + } } From b0175719b93a4297d842b569887b5f214f779f96 Mon Sep 17 00:00:00 2001 From: Godwin Asuquo Date: Wed, 1 Mar 2023 23:44:15 +0200 Subject: [PATCH 14/21] Fix issues from code review --- .github/workflows/pr_test_build.yml | 8 -- lib/entities/default_settings_migration.dart | 16 +++- lib/entities/exchange_api_mode.dart | 39 +++++++++ lib/exchange/exchange_provider.dart | 2 +- .../trocador/trocador_exchange_provider.dart | 24 ++++-- .../advanced_privacy_settings_page.dart | 85 +++++++++++-------- lib/src/screens/settings/privacy_page.dart | 21 +++-- lib/store/settings_store.dart | 15 ++-- .../advanced_privacy_settings_view_model.dart | 13 ++- .../exchange/exchange_view_model.dart | 10 ++- .../settings/privacy_settings_view_model.dart | 15 ++-- 11 files changed, 165 insertions(+), 83 deletions(-) create mode 100644 lib/entities/exchange_api_mode.dart 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; } } From 9f952cda4076eb28094d368d8ccf4cb3276563ac Mon Sep 17 00:00:00 2001 From: Godwin Asuquo Date: Wed, 1 Mar 2023 23:46:05 +0200 Subject: [PATCH 15/21] Remove disableExchangeKey from sharedpreference --- lib/entities/default_settings_migration.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/entities/default_settings_migration.dart b/lib/entities/default_settings_migration.dart index b1bb144be..94fc7ede8 100644 --- a/lib/entities/default_settings_migration.dart +++ b/lib/entities/default_settings_migration.dart @@ -514,4 +514,6 @@ Future migrateExchangeStatus(SharedPreferences sharedPreferences) async { await sharedPreferences.setInt(PreferencesKey.exchangeStatusKey, isExchangeDisabled ? ExchangeApiMode.disabled.raw : ExchangeApiMode.enabled.raw); + + await sharedPreferences.remove(PreferencesKey.disableExchangeKey); } From a6b03c4a8189b5b3ab75cafbd3493ab8f4a8647f Mon Sep 17 00:00:00 2001 From: Godwin Asuquo Date: Wed, 1 Mar 2023 23:51:44 +0200 Subject: [PATCH 16/21] Add file to dependecies --- pubspec_base.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/pubspec_base.yaml b/pubspec_base.yaml index 2caa9052f..7ff933520 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -56,6 +56,7 @@ dependencies: archive: ^3.3.0 cryptography: ^2.0.5 file_picker: ^4.6.1 + file: ^6.1.4 unorm_dart: ^0.2.0 # check unorm_dart for usage and for replace permission_handler: ^10.0.0 From d4b5f24d5e88422e6e1486e78e3599fb19ed8c0c Mon Sep 17 00:00:00 2001 From: Godwin Asuquo Date: Thu, 2 Mar 2023 00:00:47 +0200 Subject: [PATCH 17/21] Add file to dependecies to core --- cw_core/pubspec.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/cw_core/pubspec.yaml b/cw_core/pubspec.yaml index 50503361c..e33aeb803 100644 --- a/cw_core/pubspec.yaml +++ b/cw_core/pubspec.yaml @@ -13,6 +13,7 @@ dependencies: flutter: sdk: flutter http: ^0.13.4 + file: ^6.1.4 path_provider: ^2.0.11 mobx: ^2.0.7+4 flutter_mobx: ^2.0.6+1 From 15237d5f799953c7c317325869603d6ce1c328ac Mon Sep 17 00:00:00 2001 From: Godwin Asuquo Date: Thu, 2 Mar 2023 17:13:25 +0200 Subject: [PATCH 18/21] Fix issues from code review --- lib/core/backup_service.dart | 10 +++++----- lib/exchange/trocador/trocador_exchange_provider.dart | 2 +- .../new_wallet/advanced_privacy_settings_page.dart | 2 +- lib/src/screens/settings/privacy_page.dart | 2 +- .../advanced_privacy_settings_view_model.dart | 2 +- lib/view_model/dashboard/dashboard_view_model.dart | 3 ++- lib/view_model/exchange/exchange_view_model.dart | 11 ++++++----- .../settings/privacy_settings_view_model.dart | 2 +- pubspec_base.yaml | 1 - 9 files changed, 18 insertions(+), 17 deletions(-) diff --git a/lib/core/backup_service.dart b/lib/core/backup_service.dart index ffcb9eb4c..0439e9fb4 100644 --- a/lib/core/backup_service.dart +++ b/lib/core/backup_service.dart @@ -217,7 +217,7 @@ class BackupService { final fiatApiMode = data[PreferencesKey.currentFiatApiModeKey] as int?; final currentPinLength = data[PreferencesKey.currentPinLength] as int?; final currentTheme = data[PreferencesKey.currentTheme] as int?; - final disableExchange = data[PreferencesKey.disableExchangeKey] as bool?; + final exchangeStatus = data[PreferencesKey.exchangeStatusKey] as bool?; final currentDefaultSettingsMigrationVersion = data[PreferencesKey.currentDefaultSettingsMigrationVersion] as int?; final moneroTransactionPriority = data[PreferencesKey.moneroTransactionPriority] as int?; final bitcoinTransactionPriority = data[PreferencesKey.bitcoinTransactionPriority] as int?; @@ -280,9 +280,9 @@ class BackupService { await _sharedPreferences.setInt( PreferencesKey.currentTheme, currentTheme); - if (disableExchange != null) + if (exchangeStatus != null) await _sharedPreferences.setBool( - PreferencesKey.disableExchangeKey, disableExchange); + PreferencesKey.exchangeStatusKey, exchangeStatus); if (currentDefaultSettingsMigrationVersion != null) await _sharedPreferences.setInt( @@ -431,8 +431,8 @@ class BackupService { _sharedPreferences.getInt(PreferencesKey.displayActionListModeKey), PreferencesKey.currentTheme: _sharedPreferences.getInt(PreferencesKey.currentTheme), - PreferencesKey.disableExchangeKey: - _sharedPreferences.getBool(PreferencesKey.disableExchangeKey), + PreferencesKey.exchangeStatusKey: + _sharedPreferences.getBool(PreferencesKey.exchangeStatusKey), PreferencesKey.currentDefaultSettingsMigrationVersion: _sharedPreferences .getInt(PreferencesKey.currentDefaultSettingsMigrationVersion), PreferencesKey.bitcoinTransactionPriority: diff --git a/lib/exchange/trocador/trocador_exchange_provider.dart b/lib/exchange/trocador/trocador_exchange_provider.dart index 750ffb98a..e289b4e0b 100644 --- a/lib/exchange/trocador/trocador_exchange_provider.dart +++ b/lib/exchange/trocador/trocador_exchange_provider.dart @@ -308,7 +308,7 @@ class TrocadorExchangeProvider extends ExchangeProvider { return onionApiAuthority; } - final uri = Uri.https(onionApiAuthority, '/api/trade'); + final uri = Uri.https(onionApiAuthority, tradePath); await get(uri); return onionApiAuthority; 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 42ba0debb..a82ddaf4e 100644 --- a/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart +++ b/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart @@ -70,7 +70,7 @@ class _AdvancedPrivacySettingsBodyState extends State - widget.privacySettingsViewModel.setEnableExchange(mode), + widget.privacySettingsViewModel.setExchangeApiMode(mode), ), ); } diff --git a/lib/src/screens/settings/privacy_page.dart b/lib/src/screens/settings/privacy_page.dart index cc6acc826..5322c488f 100644 --- a/lib/src/screens/settings/privacy_page.dart +++ b/lib/src/screens/settings/privacy_page.dart @@ -36,7 +36,7 @@ class PrivacyPage extends BasePage { title: S.current.exchange, items: ExchangeApiMode.all, selectedItem: _privacySettingsViewModel.exchangeStatus, - onItemSelected: (ExchangeApiMode mode) => _privacySettingsViewModel.setEnableExchange(mode), + onItemSelected: (ExchangeApiMode mode) => _privacySettingsViewModel.setExchangeApiMode(mode), ), ), SettingsSwitcherCell( diff --git a/lib/view_model/advanced_privacy_settings_view_model.dart b/lib/view_model/advanced_privacy_settings_view_model.dart index 1bc2ecd9f..fad5fff34 100644 --- a/lib/view_model/advanced_privacy_settings_view_model.dart +++ b/lib/view_model/advanced_privacy_settings_view_model.dart @@ -38,7 +38,7 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store { } @action - void setEnableExchange(ExchangeApiMode value) { + void setExchangeApiMode(ExchangeApiMode value) { _settingsStore.exchangeStatus = value; } diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index dab2bafd1..4bc6e577d 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_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/wallet_type_utils.dart'; import 'package:cw_core/transaction_history.dart'; @@ -274,7 +275,7 @@ abstract class DashboardViewModelBase with Store { settingsStore.shouldShowYatPopup = shouldShow; @computed - bool get isEnabledExchangeAction => settingsStore.exchangeStatus != FiatApiMode.disabled; + bool get isEnabledExchangeAction => settingsStore.exchangeStatus != ExchangeApiMode.disabled; @observable bool hasExchangeAction; diff --git a/lib/view_model/exchange/exchange_view_model.dart b/lib/view_model/exchange/exchange_view_model.dart index 20a5a8e76..d0698990c 100644 --- a/lib/view_model/exchange/exchange_view_model.dart +++ b/lib/view_model/exchange/exchange_view_model.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:collection'; import 'dart:convert'; +import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/exchange/sideshift/sideshift_exchange_provider.dart'; @@ -56,7 +57,7 @@ abstract class ExchangeViewModelBase with Store { isDepositAddressEnabled = false, isReceiveAddressEnabled = false, isReceiveAmountEditable = false, - _providerUseTorOnly = false, + _useTorOnly = false, receiveCurrencies = [], depositCurrencies = [], limits = Limits(min: 0, max: 0), @@ -66,7 +67,7 @@ abstract class ExchangeViewModelBase with Store { depositCurrency = wallet.currency, providerList = [], selectedProviders = ObservableList() { - _providerUseTorOnly = _settingsStore.exchangeStatus == FiatApiMode.torOnly; + _useTorOnly = _settingsStore.exchangeStatus == ExchangeApiMode.torOnly; _setProviders(); const excludeDepositCurrencies = [CryptoCurrency.btt, CryptoCurrency.nano]; const excludeReceiveCurrencies = [CryptoCurrency.xlm, CryptoCurrency.xrp, @@ -123,7 +124,7 @@ abstract class ExchangeViewModelBase with Store { _calculateBestRate(); }); } - bool _providerUseTorOnly; + bool _useTorOnly; final WalletBase wallet; final Box trades; final ExchangeTemplateStore _exchangeTemplateStore; @@ -134,7 +135,7 @@ abstract class ExchangeViewModelBase with Store { ChangeNowExchangeProvider(), SideShiftExchangeProvider(), SimpleSwapExchangeProvider(), - TrocadorExchangeProvider(useTorOnly: _providerUseTorOnly), + TrocadorExchangeProvider(useTorOnly: _useTorOnly), ]; @observable @@ -696,7 +697,7 @@ abstract class ExchangeViewModelBase with Store { } void _setProviders(){ - if (_settingsStore.exchangeStatus == FiatApiMode.torOnly) { + if (_settingsStore.exchangeStatus == ExchangeApiMode.torOnly) { 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 1919b1944..1d58fc323 100644 --- a/lib/view_model/settings/privacy_settings_view_model.dart +++ b/lib/view_model/settings/privacy_settings_view_model.dart @@ -25,7 +25,7 @@ abstract class PrivacySettingsViewModelBase with Store { void setShouldSaveRecipientAddress(bool value) => _settingsStore.shouldSaveRecipientAddress = value; @action - void setEnableExchange(ExchangeApiMode value) => _settingsStore.exchangeStatus = value; + void setExchangeApiMode(ExchangeApiMode value) => _settingsStore.exchangeStatus = value; @action void setFiatMode(bool value) { diff --git a/pubspec_base.yaml b/pubspec_base.yaml index 7ff933520..2caa9052f 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -56,7 +56,6 @@ dependencies: archive: ^3.3.0 cryptography: ^2.0.5 file_picker: ^4.6.1 - file: ^6.1.4 unorm_dart: ^0.2.0 # check unorm_dart for usage and for replace permission_handler: ^10.0.0 From d5c4bd0236032878f138ffbd8ad3625dfacf112d Mon Sep 17 00:00:00 2001 From: Godwin Asuquo Date: Thu, 2 Mar 2023 17:43:42 +0200 Subject: [PATCH 19/21] change type to int --- lib/core/backup_service.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/core/backup_service.dart b/lib/core/backup_service.dart index 0439e9fb4..3cb434efe 100644 --- a/lib/core/backup_service.dart +++ b/lib/core/backup_service.dart @@ -217,7 +217,7 @@ class BackupService { final fiatApiMode = data[PreferencesKey.currentFiatApiModeKey] as int?; final currentPinLength = data[PreferencesKey.currentPinLength] as int?; final currentTheme = data[PreferencesKey.currentTheme] as int?; - final exchangeStatus = data[PreferencesKey.exchangeStatusKey] as bool?; + final exchangeStatus = data[PreferencesKey.exchangeStatusKey] as int?; final currentDefaultSettingsMigrationVersion = data[PreferencesKey.currentDefaultSettingsMigrationVersion] as int?; final moneroTransactionPriority = data[PreferencesKey.moneroTransactionPriority] as int?; final bitcoinTransactionPriority = data[PreferencesKey.bitcoinTransactionPriority] as int?; @@ -281,7 +281,7 @@ class BackupService { PreferencesKey.currentTheme, currentTheme); if (exchangeStatus != null) - await _sharedPreferences.setBool( + await _sharedPreferences.setInt( PreferencesKey.exchangeStatusKey, exchangeStatus); if (currentDefaultSettingsMigrationVersion != null) @@ -432,7 +432,7 @@ class BackupService { PreferencesKey.currentTheme: _sharedPreferences.getInt(PreferencesKey.currentTheme), PreferencesKey.exchangeStatusKey: - _sharedPreferences.getBool(PreferencesKey.exchangeStatusKey), + _sharedPreferences.getInt(PreferencesKey.exchangeStatusKey), PreferencesKey.currentDefaultSettingsMigrationVersion: _sharedPreferences .getInt(PreferencesKey.currentDefaultSettingsMigrationVersion), PreferencesKey.bitcoinTransactionPriority: From 29e9bb2181ef60e419d67938f62afa8e5d39ca9c Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Thu, 2 Mar 2023 19:24:52 +0200 Subject: [PATCH 20/21] Call Onion API from http --- .../trocador/trocador_exchange_provider.dart | 79 ++++++++----------- 1 file changed, 35 insertions(+), 44 deletions(-) diff --git a/lib/exchange/trocador/trocador_exchange_provider.dart b/lib/exchange/trocador/trocador_exchange_provider.dart index e289b4e0b..1c2a85163 100644 --- a/lib/exchange/trocador/trocador_exchange_provider.dart +++ b/lib/exchange/trocador/trocador_exchange_provider.dart @@ -16,7 +16,7 @@ class TrocadorExchangeProvider extends ExchangeProvider { TrocadorExchangeProvider({this.useTorOnly = false}) : _lastUsedRateId = '', super(pairList: _supportedPairs()); - + bool useTorOnly; static const List _notSupported = [ @@ -85,9 +85,7 @@ class TrocadorExchangeProvider extends ExchangeProvider { params['id'] = _lastUsedRateId; } - final String apiAuthority = await _getAuthority(); - - final uri = Uri.https(apiAuthority, createTradePath, params); + final uri = await _getUri(createTradePath, params); final response = await get(uri); if (response.statusCode == 400) { @@ -137,31 +135,28 @@ class TrocadorExchangeProvider extends ExchangeProvider { {required CryptoCurrency from, required CryptoCurrency to, required bool isFixedRateMode}) async { - - final params = { - 'api_key': apiKey, - 'ticker': from.title.toLowerCase(), - 'name': from.name, - }; - - final String apiAuthority = await _getAuthority(); - final uri = Uri.https(apiAuthority, coinPath, params); - - final response = await get(uri); + final params = { + 'api_key': apiKey, + 'ticker': from.title.toLowerCase(), + 'name': from.name, + }; - if (response.statusCode != 200) { - throw Exception('Unexpected http status: ${response.statusCode}'); - } + final uri = await _getUri(coinPath, params); + + final response = await get(uri); + + if (response.statusCode != 200) { + throw Exception('Unexpected http status: ${response.statusCode}'); + } + + final responseJSON = json.decode(response.body) as List; + + if (responseJSON.isEmpty) { + throw Exception('No data'); + } + + final coinJson = responseJSON.first as Map; - final responseJSON = json.decode(response.body) as List; - - if (responseJSON.isEmpty) { - throw Exception('No data'); - } - - final coinJson = responseJSON.first as Map; - - return Limits( min: coinJson['minimum'] as double, max: coinJson['maximum'] as double, @@ -180,8 +175,6 @@ class TrocadorExchangeProvider extends ExchangeProvider { return 0.0; } - final String apiAuthority = await _getAuthority(); - final params = { 'api_key': apiKey, 'ticker_from': from.title.toLowerCase(), @@ -196,7 +189,7 @@ class TrocadorExchangeProvider extends ExchangeProvider { 'best_only': 'True', }; - final uri = Uri.https(apiAuthority, newRatePath, params); + final uri = await _getUri(newRatePath, params); final response = await get(uri); final responseJSON = json.decode(response.body) as Map; final fromAmount = double.parse(responseJSON['amount_from'].toString()); @@ -216,8 +209,7 @@ class TrocadorExchangeProvider extends ExchangeProvider { @override Future findTradeById({required String id}) async { - final String apiAuthority = await _getAuthority(); - final uri = Uri.https(apiAuthority, tradePath, {'api_key': apiKey, 'id': id}); + final uri = await _getUri(tradePath, {'api_key': apiKey, 'id': id}); return get(uri).then((response) { if (response.statusCode != 200) { throw Exception('Unexpected http status: ${response.statusCode}'); @@ -298,23 +290,22 @@ class TrocadorExchangeProvider extends ExchangeProvider { } } - Future _getAuthority() async { - if(!supportsOnionAddress){ - return clearNetAuthority; - } + Future _getUri(String createTradePath, Map queryParams) async { + if (!supportsOnionAddress) { + return Uri.https(clearNetAuthority, tradePath, queryParams); + } + + if (useTorOnly) { + return Uri.http(onionApiAuthority, tradePath, queryParams); + } try { - if (useTorOnly) { - return onionApiAuthority; - } - - final uri = Uri.https(onionApiAuthority, tradePath); + final uri = Uri.http(onionApiAuthority, tradePath); await get(uri); - return onionApiAuthority; + return Uri.http(onionApiAuthority, tradePath, queryParams); } catch (e) { - - return clearNetAuthority; + return Uri.https(clearNetAuthority, tradePath, queryParams); } } } From 60f47e5e9fed8d83252014a4814eca045b6fd0b5 Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Thu, 2 Mar 2023 21:13:19 +0200 Subject: [PATCH 21/21] Fix calling wrong path --- .../trocador/trocador_exchange_provider.dart | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/exchange/trocador/trocador_exchange_provider.dart b/lib/exchange/trocador/trocador_exchange_provider.dart index 1c2a85163..98726a265 100644 --- a/lib/exchange/trocador/trocador_exchange_provider.dart +++ b/lib/exchange/trocador/trocador_exchange_provider.dart @@ -290,22 +290,23 @@ class TrocadorExchangeProvider extends ExchangeProvider { } } - Future _getUri(String createTradePath, Map queryParams) async { + Future _getUri(String path, Map queryParams) async { if (!supportsOnionAddress) { - return Uri.https(clearNetAuthority, tradePath, queryParams); + return Uri.https(clearNetAuthority, path, queryParams); } + final uri = Uri.http(onionApiAuthority, path, queryParams); + if (useTorOnly) { - return Uri.http(onionApiAuthority, tradePath, queryParams); + return uri; } try { - final uri = Uri.http(onionApiAuthority, tradePath); await get(uri); - return Uri.http(onionApiAuthority, tradePath, queryParams); + return uri; } catch (e) { - return Uri.https(clearNetAuthority, tradePath, queryParams); + return Uri.https(clearNetAuthority, path, queryParams); } } }