Generic fixes (#1245)

* Remove error message if buy action is disabled * Fix wallet list selected wallet issue * Check if the widget is still mounted before showing popup * minor code readability enhancement [skip ci] * Code enhancement [skip ci] * Revert removing ask each time localization * Add Moonpay to sell flow Code Enhancements * remove error popup when sell option is disabled

Omar Hatem committed Jan 1, 2024 at 15:05 UTC 133fa2987a48fd1f6ac77dd57c202f64a6d75619
23 files changed +270 -313
cw_ethereum/lib/ethereum_wallet_service.dart
+3 -8
@@ -20,12 +20,7 @@ class EthereumWalletService extends WalletService<EthereumNewWalletCredentials,
20
21 @override
22 Future<EthereumWallet> create(EthereumNewWalletCredentials credentials) async {
23 -
24 - final strength = (credentials.seedPhraseLength == 12)
25 - ? 128
26 - : (credentials.seedPhraseLength == 24)
27 - ? 256
28 - : 128;
23 + final strength = credentials.seedPhraseLength == 24 ? 256 : 128;
24
25 final mnemonic = bip39.generateMnemonic(strength: strength);
26 final wallet = EthereumWallet(
@@ -67,8 +62,8 @@ class EthereumWalletService extends WalletService<EthereumNewWalletCredentials,
62 @override
63 Future<void> remove(String wallet) async {
64 File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true);
70 - final walletInfo = walletInfoSource.values.firstWhereOrNull(
71 - (info) => info.id == WalletBase.idFor(wallet, getType()))!;
65 + final walletInfo = walletInfoSource.values
66 + .firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
67 await walletInfoSource.delete(walletInfo.key);
68 }
69
cw_polygon/lib/polygon_wallet_service.dart
+1 -5
@@ -20,11 +20,7 @@ class PolygonWalletService extends WalletService<PolygonNewWalletCredentials,
20
21 @override
22 Future<PolygonWallet> create(PolygonNewWalletCredentials credentials) async {
23 - final strength = (credentials.seedPhraseLength == 12)
24 - ? 128
25 - : (credentials.seedPhraseLength == 24)
26 - ? 256
27 - : 128;
23 + final strength = credentials.seedPhraseLength == 24 ? 256 : 128;
24
25 final mnemonic = bip39.generateMnemonic(strength: strength);
26 final wallet = PolygonWallet(
lib/buy/buy_provider.dart
+1 -3
@@ -14,9 +14,7 @@ abstract class BuyProvider {
14
15 String get title;
16
17 - String get buyOptionDescription;
18 -
19 - String get sellOptionDescription;
17 + String get providerDescription;
18
19 String get lightIcon;
20
lib/buy/dfx/dfx_buy_provider.dart
+2 -4
@@ -26,10 +26,7 @@ class DFXBuyProvider extends BuyProvider {
26 String get title => 'DFX Connect';
27
28 @override
29 - String get buyOptionDescription => S.current.dfx_option_description;
30 -
31 - @override
32 - String get sellOptionDescription => S.current.dfx_option_description;
29 + String get providerDescription => S.current.dfx_option_description;
30
31 @override
32 String get lightIcon => 'assets/images/dfx_light.png';
@@ -152,6 +149,7 @@ class DFXBuyProvider extends BuyProvider {
149 }
150 }
151
152 + @override
153 Future<void> launchProvider(BuildContext context, bool? isBuyAction) async {
154 try {
155 final assetOut = this.assetOut;
lib/buy/moonpay/moonpay_provider.dart renamed
+68 -54
@@ -21,19 +21,33 @@ import 'package:cake_wallet/.secrets.g.dart' as secrets;
21 import 'package:cw_core/crypto_currency.dart';
22 import 'package:url_launcher/url_launcher.dart';
23
24 -class MoonPaySellProvider {
25 - MoonPaySellProvider({required SettingsStore settingsStore,
26 - required WalletBase wallet, this.isTest = false})
27 - : baseUrl = isTest ? _baseTestUrl : _baseProductUrl,
24 +class MoonPaySellProvider extends BuyProvider {
25 + MoonPaySellProvider({
26 + required SettingsStore settingsStore,
27 + required WalletBase wallet,
28 + bool isTestEnvironment = false,
29 + }) : baseUrl = isTestEnvironment ? _baseTestUrl : _baseProductUrl,
30 this._settingsStore = settingsStore,
29 - this._wallet = wallet;
31 + super(wallet: wallet, isTestEnvironment: isTestEnvironment);
32
33 final SettingsStore _settingsStore;
32 - final WalletBase _wallet;
34
35 static const _baseTestUrl = 'sell-sandbox.moonpay.com';
36 static const _baseProductUrl = 'sell.moonpay.com';
37
38 + @override
39 + String get providerDescription =>
40 + 'MoonPay offers a fast and simple way to buy and sell cryptocurrencies';
41 +
42 + @override
43 + String get title => 'MoonPay';
44 +
45 + @override
46 + String get lightIcon => 'assets/images/moonpay_light.png';
47 +
48 + @override
49 + String get darkIcon => 'assets/images/moonpay_dark.png';
50 +
51 static String themeToMoonPayTheme(ThemeBase theme) {
52 switch (theme.type) {
53 case ThemeType.bright:
@@ -45,11 +59,11 @@ class MoonPaySellProvider {
59 }
60
61 static String get _apiKey => secrets.moonPayApiKey;
62 +
63 static String get _secretKey => secrets.moonPaySecretKey;
49 - final bool isTest;
64 final String baseUrl;
65
52 - Future<Uri> requestUrl({
66 + Future<Uri> requestMoonPayUrl({
67 required CryptoCurrency currency,
68 required String refundWalletAddress,
69 required SettingsStore settingsStore,
@@ -78,7 +92,7 @@ class MoonPaySellProvider {
92 final digest = hmac.convert(messageBytes);
93 final signature = base64.encode(digest.bytes);
94
81 - if (isTest) {
95 + if (isTestEnvironment) {
96 return originalUri;
97 }
98
@@ -88,11 +102,12 @@ class MoonPaySellProvider {
102 return signedUri;
103 }
104
91 - Future<void> launchProvider(BuildContext context) async {
105 + @override
106 + Future<void> launchProvider(BuildContext context, bool? isBuyAction) async {
107 try {
93 - final uri = await requestUrl(
94 - currency: _wallet.currency,
95 - refundWalletAddress: _wallet.walletAddresses.address,
108 + final uri = await requestMoonPayUrl(
109 + currency: wallet.currency,
110 + refundWalletAddress: wallet.walletAddresses.address,
111 settingsStore: _settingsStore,
112 );
113
@@ -137,35 +152,43 @@ class MoonPayBuyProvider extends BuyProvider {
152 static const _secretKey = secrets.moonPaySecretKey;
153
154 @override
140 - String get title => 'Moon Pay';
155 + String get title => 'MoonPay';
156
157 @override
143 - String get buyOptionDescription => '';
158 + String get providerDescription =>
159 + 'MoonPay offers a fast and simple way to buy and sell cryptocurrencies';
160
161 @override
162 String get lightIcon => 'assets/images/moonpay_light.png';
163
164 @override
165 String get darkIcon => 'assets/images/moonpay_dark.png';
150 -
151 - String get currencyCode =>
152 - walletTypeToCryptoCurrency(wallet.type).title.toLowerCase();
166 +
167 + String get currencyCode => walletTypeToCryptoCurrency(wallet.type).title.toLowerCase();
168
169 String get trackUrl => baseUrl + '/transaction_receipt?transactionId=';
170
171 String baseUrl;
172
173 Future<String> requestUrl(String amount, String sourceCurrency) async {
159 - final enabledPaymentMethods =
160 - 'credit_debit_card%2Capple_pay%2Cgoogle_pay%2Csamsung_pay'
174 + final enabledPaymentMethods = 'credit_debit_card%2Capple_pay%2Cgoogle_pay%2Csamsung_pay'
175 '%2Csepa_bank_transfer%2Cgbp_bank_transfer%2Cgbp_open_banking_payment';
176
163 - final suffix = '?apiKey=' + _apiKey + '&currencyCode=' +
164 - currencyCode + '&enabledPaymentMethods=' + enabledPaymentMethods +
165 - '&walletAddress=' + wallet.walletAddresses.address +
166 - '&baseCurrencyCode=' + sourceCurrency.toLowerCase() +
167 - '&baseCurrencyAmount=' + amount + '&lockAmount=true' +
168 - '&showAllCurrencies=false' + '&showWalletAddressForm=false';
177 + final suffix = '?apiKey=' +
178 + _apiKey +
179 + '&currencyCode=' +
180 + currencyCode +
181 + '&enabledPaymentMethods=' +
182 + enabledPaymentMethods +
183 + '&walletAddress=' +
184 + wallet.walletAddresses.address +
185 + '&baseCurrencyCode=' +
186 + sourceCurrency.toLowerCase() +
187 + '&baseCurrencyAmount=' +
188 + amount +
189 + '&lockAmount=true' +
190 + '&showAllCurrencies=false' +
191 + '&showWalletAddressForm=false';
192
193 final originalUrl = baseUrl + suffix;
194
@@ -174,24 +197,27 @@ class MoonPayBuyProvider extends BuyProvider {
197 final hmac = Hmac(sha256, key);
198 final digest = hmac.convert(messageBytes);
199 final signature = base64.encode(digest.bytes);
177 - final urlWithSignature = originalUrl +
178 - '&signature=${Uri.encodeComponent(signature)}';
200 + final urlWithSignature = originalUrl + '&signature=${Uri.encodeComponent(signature)}';
201
202 return isTestEnvironment ? originalUrl : urlWithSignature;
203 }
204
205 Future<BuyAmount> calculateAmount(String amount, String sourceCurrency) async {
184 - final url = _apiUrl + _currenciesSuffix + '/$currencyCode' +
185 - _quoteSuffix + '/?apiKey=' + _apiKey +
186 - '&baseCurrencyAmount=' + amount +
187 - '&baseCurrencyCode=' + sourceCurrency.toLowerCase();
206 + final url = _apiUrl +
207 + _currenciesSuffix +
208 + '/$currencyCode' +
209 + _quoteSuffix +
210 + '/?apiKey=' +
211 + _apiKey +
212 + '&baseCurrencyAmount=' +
213 + amount +
214 + '&baseCurrencyCode=' +
215 + sourceCurrency.toLowerCase();
216 final uri = Uri.parse(url);
217 final response = await get(uri);
218
219 if (response.statusCode != 200) {
192 - throw BuyException(
193 - title: buyOptionDescription,
194 - content: 'Quote is not found!');
220 + throw BuyException(title: providerDescription, content: 'Quote is not found!');
221 }
222
223 final responseJSON = json.decode(response.body) as Map<String, dynamic>;
@@ -200,21 +226,16 @@ class MoonPayBuyProvider extends BuyProvider {
226 final minSourceAmount = responseJSON['baseCurrency']['minAmount'] as int;
227
228 return BuyAmount(
203 - sourceAmount: sourceAmount,
204 - destAmount: destAmount,
205 - minAmount: minSourceAmount);
229 + sourceAmount: sourceAmount, destAmount: destAmount, minAmount: minSourceAmount);
230 }
231
232 Future<Order> findOrderById(String id) async {
209 - final url = _apiUrl + _transactionsSuffix + '/$id' +
210 - '?apiKey=' + _apiKey;
233 + final url = _apiUrl + _transactionsSuffix + '/$id' + '?apiKey=' + _apiKey;
234 final uri = Uri.parse(url);
235 final response = await get(uri);
236
237 if (response.statusCode != 200) {
215 - throw BuyException(
216 - title: buyOptionDescription,
217 - content: 'Transaction $id is not found!');
238 + throw BuyException(title: providerDescription, content: 'Transaction $id is not found!');
239 }
240
241 final responseJSON = json.decode(response.body) as Map<String, dynamic>;
@@ -232,8 +253,7 @@ class MoonPayBuyProvider extends BuyProvider {
253 createdAt: createdAt,
254 amount: amount.toString(),
255 receiveAddress: wallet.walletAddresses.address,
235 - walletId: wallet.id
236 - );
256 + walletId: wallet.id);
257 }
258
259 static Future<bool> onEnabled() async {
@@ -254,12 +274,6 @@ class MoonPayBuyProvider extends BuyProvider {
274 }
275
276 @override
257 - // TODO: implement sellOptionDescription
258 - String get sellOptionDescription => throw UnimplementedError();
259 -
260 - @override
261 - Future<void> launchProvider(BuildContext context, bool? isBuyAction) {
262 - // TODO: implement launchProvider
263 - throw UnimplementedError();
264 - }
265 -}
\ No newline at end of file
277 + Future<void> launchProvider(BuildContext context, bool? isBuyAction) =>
278 + throw UnimplementedError();
279 +}
lib/buy/onramper/onramper_buy_provider.dart
+1 -4
@@ -23,10 +23,7 @@ class OnRamperBuyProvider extends BuyProvider {
23 String get title => 'Onramper';
24
25 @override
26 - String get buyOptionDescription => S.current.onramper_option_description;
27 -
28 - @override
29 - String get sellOptionDescription => S.current.onramper_option_description;
26 + String get providerDescription => S.current.onramper_option_description;
27
28 @override
29 String get lightIcon => 'assets/images/onramper_light.png';
lib/buy/robinhood/robinhood_buy_provider.dart
+2 -10
@@ -1,10 +1,7 @@
1 import 'dart:convert';
2
3 import 'package:cake_wallet/.secrets.g.dart' as secrets;
4 -import 'package:cake_wallet/buy/buy_amount.dart';
4 import 'package:cake_wallet/buy/buy_provider.dart';
6 -import 'package:cake_wallet/buy/buy_provider_description.dart';
7 -import 'package:cake_wallet/buy/order.dart';
5 import 'package:cake_wallet/generated/i18n.dart';
6 import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
7 import 'package:cake_wallet/utils/show_pop_up.dart';
@@ -14,22 +11,18 @@ import 'package:flutter/material.dart';
11 import 'package:http/http.dart' as http;
12 import 'package:url_launcher/url_launcher.dart';
13
17 -class RobinhoodBuyProvider extends BuyProvider{
14 +class RobinhoodBuyProvider extends BuyProvider {
15 RobinhoodBuyProvider({required WalletBase wallet, bool isTestEnvironment = false})
16 : super(wallet: wallet, isTestEnvironment: isTestEnvironment);
17
18 static const _baseUrl = 'applink.robinhood.com';
19 static const _cIdBaseUrl = 'exchange-helper.cakewallet.com';
20
24 -
21 @override
22 String get title => 'Robinhood Connect';
23
24 @override
29 - String get buyOptionDescription => S.current.robinhood_option_description;
30 -
31 - @override
32 - String get sellOptionDescription => S.current.robinhood_option_description;
25 + String get providerDescription => S.current.robinhood_option_description;
26
27 @override
28 String get lightIcon => 'assets/images/robinhood_light.png';
@@ -105,5 +98,4 @@ class RobinhoodBuyProvider extends BuyProvider{
98 });
99 }
100 }
108 -
101 }
lib/buy/wyre/wyre_buy_provider.dart
+17 -38
@@ -13,10 +13,8 @@ import 'package:cake_wallet/.secrets.g.dart' as secrets;
13
14 class WyreBuyProvider extends BuyProvider {
15 WyreBuyProvider({required WalletBase wallet, bool isTestEnvironment = false})
16 - : baseApiUrl = isTestEnvironment
17 - ? _baseTestApiUrl
18 - : _baseProductApiUrl,
19 - super(wallet: wallet, isTestEnvironment: isTestEnvironment);
16 + : baseApiUrl = isTestEnvironment ? _baseTestApiUrl : _baseProductApiUrl,
17 + super(wallet: wallet, isTestEnvironment: isTestEnvironment);
18
19 static const _baseTestApiUrl = 'https://api.testwyre.com';
20 static const _baseProductApiUrl = 'https://api.sendwyre.com';
@@ -36,7 +34,7 @@ class WyreBuyProvider extends BuyProvider {
34 String get title => 'Wyre';
35
36 @override
39 - String get buyOptionDescription => '';
37 + String get providerDescription => '';
38
39 @override
40 String get lightIcon => 'assets/images/robinhood_light.png';
@@ -44,16 +42,13 @@ class WyreBuyProvider extends BuyProvider {
42 @override
43 String get darkIcon => 'assets/images/robinhood_dark.png';
44
47 - String get trackUrl => isTestEnvironment
48 - ? _trackTestUrl
49 - : _trackProductUrl;
45 + String get trackUrl => isTestEnvironment ? _trackTestUrl : _trackProductUrl;
46
47 String baseApiUrl;
48
49 Future<String> requestUrl(String amount, String sourceCurrency) async {
50 final timestamp = DateTime.now().millisecondsSinceEpoch.toString();
55 - final url = baseApiUrl + _ordersSuffix + _reserveSuffix +
56 - _timeStampSuffix + timestamp;
51 + final url = baseApiUrl + _ordersSuffix + _reserveSuffix + _timeStampSuffix + timestamp;
52 final uri = Uri.parse(url);
53 final body = {
54 'amount': amount,
@@ -72,9 +67,7 @@ class WyreBuyProvider extends BuyProvider {
67 body: json.encode(body));
68
69 if (response.statusCode != 200) {
75 - throw BuyException(
76 - title: buyOptionDescription,
77 - content: 'Url $url is not found!');
70 + throw BuyException(title: providerDescription, content: 'Url $url is not found!');
71 }
72
73 final responseJSON = json.decode(response.body) as Map<String, dynamic>;
@@ -102,9 +95,7 @@ class WyreBuyProvider extends BuyProvider {
95 body: json.encode(body));
96
97 if (response.statusCode != 200) {
105 - throw BuyException(
106 - title: buyOptionDescription,
107 - content: 'Quote is not found!');
98 + throw BuyException(title: providerDescription, content: 'Quote is not found!');
99 }
100
101 final responseJSON = json.decode(response.body) as Map<String, dynamic>;
@@ -112,7 +103,8 @@ class WyreBuyProvider extends BuyProvider {
103 final destAmount = responseJSON['destAmount'] as double;
104 final achAmount = responseJSON['sourceAmountWithoutFees'] as double;
105
115 - return BuyAmount(sourceAmount: sourceAmount, destAmount: destAmount, achSourceAmount: achAmount);
106 + return BuyAmount(
107 + sourceAmount: sourceAmount, destAmount: destAmount, achSourceAmount: achAmount);
108 }
109
110 Future<Order> findOrderById(String id) async {
@@ -121,35 +113,27 @@ class WyreBuyProvider extends BuyProvider {
113 final orderResponse = await get(orderUri);
114
115 if (orderResponse.statusCode != 200) {
124 - throw BuyException(
125 - title: buyOptionDescription,
126 - content: 'Order $id is not found!');
116 + throw BuyException(title: providerDescription, content: 'Order $id is not found!');
117 }
118
129 - final orderResponseJSON =
130 - json.decode(orderResponse.body) as Map<String, dynamic>;
119 + final orderResponseJSON = json.decode(orderResponse.body) as Map<String, dynamic>;
120 final transferId = orderResponseJSON['transferId'] as String;
121 final from = orderResponseJSON['sourceCurrency'] as String;
122 final to = orderResponseJSON['destCurrency'] as String;
123 final status = orderResponseJSON['status'] as String;
124 final state = TradeState.deserialize(raw: status.toLowerCase());
125 final createdAtRaw = orderResponseJSON['createdAt'] as int;
137 - final createdAt =
138 - DateTime.fromMillisecondsSinceEpoch(createdAtRaw).toLocal();
126 + final createdAt = DateTime.fromMillisecondsSinceEpoch(createdAtRaw).toLocal();
127
140 - final transferUrl =
141 - baseApiUrl + _transferSuffix + transferId + _trackSuffix;
128 + final transferUrl = baseApiUrl + _transferSuffix + transferId + _trackSuffix;
129 final transferUri = Uri.parse(transferUrl);
130 final transferResponse = await get(transferUri);
131
132 if (transferResponse.statusCode != 200) {
146 - throw BuyException(
147 - title: buyOptionDescription,
148 - content: 'Transfer $transferId is not found!');
133 + throw BuyException(title: providerDescription, content: 'Transfer $transferId is not found!');
134 }
135
151 - final transferResponseJSON =
152 - json.decode(transferResponse.body) as Map<String, dynamic>;
136 + final transferResponseJSON = json.decode(transferResponse.body) as Map<String, dynamic>;
137 final amount = transferResponseJSON['destAmount'] as double;
138
139 return Order(
@@ -162,8 +146,7 @@ class WyreBuyProvider extends BuyProvider {
146 createdAt: createdAt,
147 amount: amount.toString(),
148 receiveAddress: wallet.walletAddresses.address,
165 - walletId: wallet.id
166 - );
149 + walletId: wallet.id);
150 }
151
152 @override
@@ -171,8 +154,4 @@ class WyreBuyProvider extends BuyProvider {
154 // TODO: implement launchProvider
155 throw UnimplementedError();
156 }
174 -
175 - @override
176 - // TODO: implement sellOptionDescription
177 - String get sellOptionDescription => throw UnimplementedError();
178 -}
\ No newline at end of file
157 +}
lib/di.dart
+1 -1
@@ -1,7 +1,7 @@
1 import 'package:cake_wallet/anonpay/anonpay_api.dart';
2 import 'package:cake_wallet/anonpay/anonpay_info_base.dart';
3 import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart';
4 -import 'package:cake_wallet/buy/moonpay/moonpay_buy_provider.dart';
4 +import 'package:cake_wallet/buy/moonpay/moonpay_provider.dart';
5 import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart';
6 import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
7 import 'package:cake_wallet/buy/payfura/payfura_buy_provider.dart';
lib/entities/buy_provider_types.dart deleted
-112
@@ -1,112 +0,0 @@
1 -import 'package:cake_wallet/buy/buy_provider.dart';
2 -import 'package:cake_wallet/buy/dfx/dfx_buy_provider.dart';
3 -import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart';
4 -import 'package:cake_wallet/buy/robinhood/robinhood_buy_provider.dart';
5 -import 'package:cake_wallet/di.dart';
6 -import 'package:cw_core/wallet_type.dart';
7 -
8 -enum BuyProviderType {
9 - askEachTime,
10 - robinhood,
11 - dfx,
12 - onramper,
13 -}
14 -
15 -extension BuyProviderTypeName on BuyProviderType {
16 - String get name {
17 - switch (this) {
18 - case BuyProviderType.askEachTime:
19 - return 'Ask each time';
20 - case BuyProviderType.robinhood:
21 - return 'Robinhood Connect';
22 - case BuyProviderType.dfx:
23 - return 'DFX Connect';
24 - case BuyProviderType.onramper:
25 - return 'Onramper';
26 - default:
27 - return this.toString().split('.').last;
28 - }
29 - }
30 -
31 - String get id {
32 - switch (this) {
33 - case BuyProviderType.askEachTime:
34 - return 'ask_each_time_provider';
35 - case BuyProviderType.robinhood:
36 - return 'robinhood_connect_provider';
37 - case BuyProviderType.dfx:
38 - return 'dfx_connect_provider';
39 - case BuyProviderType.onramper:
40 - return 'onramper_provider';
41 - default:
42 - return this.toString().split('.').last.replaceAll('.', '_')
43 - .toLowerCase() + '_provider';
44 - }
45 - }
46 -}
47 -
48 -class BuyProviderHelper {
49 - static List<BuyProviderType> getAvailableBuyProviderTypes(
50 - WalletType walletType) {
51 - switch (walletType) {
52 - case WalletType.nano:
53 - case WalletType.banano:
54 - return [BuyProviderType.askEachTime, BuyProviderType.onramper];
55 - case WalletType.monero:
56 - return [
57 - BuyProviderType.askEachTime,
58 - BuyProviderType.onramper,
59 - BuyProviderType.dfx
60 - ];
61 - case WalletType.bitcoin:
62 - case WalletType.ethereum:
63 - return [
64 - BuyProviderType.askEachTime,
65 - BuyProviderType.onramper,
66 - BuyProviderType.dfx,
67 - BuyProviderType.robinhood
68 - ];
69 - case WalletType.litecoin:
70 - case WalletType.bitcoinCash:
71 - return [
72 - BuyProviderType.askEachTime,
73 - BuyProviderType.onramper,
74 - BuyProviderType.robinhood
75 - ];
76 - default:
77 - return [];
78 - }
79 - }
80 -
81 - static List<BuyProviderType> getAvailableSellProviderTypes(
82 - WalletType walletType) {
83 - switch (walletType) {
84 - case WalletType.nano:
85 - case WalletType.banano:
86 - return [BuyProviderType.askEachTime];
87 - case WalletType.monero:
88 - return [BuyProviderType.askEachTime, BuyProviderType.dfx];
89 - case WalletType.bitcoin:
90 - case WalletType.ethereum:
91 - return [BuyProviderType.askEachTime, BuyProviderType.dfx];
92 - case WalletType.litecoin:
93 - case WalletType.bitcoinCash:
94 - return [BuyProviderType.askEachTime];
95 - default:
96 - return [];
97 - }
98 - }
99 -
100 - static BuyProvider? getProviderByType(BuyProviderType type) {
101 - switch (type) {
102 - case BuyProviderType.robinhood:
103 - return getIt.get<RobinhoodBuyProvider>();
104 - case BuyProviderType.dfx:
105 - return getIt.get<DFXBuyProvider>();
106 - case BuyProviderType.onramper:
107 - return getIt.get<OnRamperBuyProvider>();
108 - case BuyProviderType.askEachTime:
109 - return null;
110 - }
111 - }
112 -}
lib/entities/main_actions.dart
-4
@@ -37,8 +37,6 @@ class MainActions {
37 canShow: (viewModel) => viewModel.hasBuyAction,
38 onTap: (BuildContext context, DashboardViewModel viewModel) async {
39 if (!viewModel.isEnabledBuyAction) {
40 - await _showErrorDialog(
41 - context, S.of(context).buy, S.of(context).unsupported_asset);
40 return;
41 }
42
@@ -88,8 +86,6 @@ class MainActions {
86 canShow: (viewModel) => viewModel.hasSellAction,
87 onTap: (BuildContext context, DashboardViewModel viewModel) async {
88 if (!viewModel.isEnabledSellAction) {
91 - await _showErrorDialog(
92 - context, S.of(context).sell, S.of(context).unsupported_asset);
89 return;
90 }
91
lib/entities/provider_types.dart new
+108
@@ -0,0 +1,108 @@
1 +import 'package:cake_wallet/buy/buy_provider.dart';
2 +import 'package:cake_wallet/buy/dfx/dfx_buy_provider.dart';
3 +import 'package:cake_wallet/buy/moonpay/moonpay_provider.dart';
4 +import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart';
5 +import 'package:cake_wallet/buy/robinhood/robinhood_buy_provider.dart';
6 +import 'package:cake_wallet/di.dart';
7 +import 'package:cw_core/wallet_type.dart';
8 +
9 +enum ProviderType {
10 + askEachTime,
11 + robinhood,
12 + dfx,
13 + onramper,
14 + moonpaySell,
15 +}
16 +
17 +extension ProviderTypeName on ProviderType {
18 + String get title {
19 + switch (this) {
20 + case ProviderType.askEachTime:
21 + return 'Ask each time';
22 + case ProviderType.robinhood:
23 + return 'Robinhood Connect';
24 + case ProviderType.dfx:
25 + return 'DFX Connect';
26 + case ProviderType.onramper:
27 + return 'Onramper';
28 + case ProviderType.moonpaySell:
29 + return 'MoonPay';
30 + }
31 + }
32 +
33 + String get id {
34 + switch (this) {
35 + case ProviderType.askEachTime:
36 + return 'ask_each_time_provider';
37 + case ProviderType.robinhood:
38 + return 'robinhood_connect_provider';
39 + case ProviderType.dfx:
40 + return 'dfx_connect_provider';
41 + case ProviderType.onramper:
42 + return 'onramper_provider';
43 + case ProviderType.moonpaySell:
44 + return 'moonpay_provider';
45 + }
46 + }
47 +}
48 +
49 +class ProvidersHelper {
50 + static List<ProviderType> getAvailableBuyProviderTypes(WalletType walletType) {
51 + switch (walletType) {
52 + case WalletType.nano:
53 + case WalletType.banano:
54 + return [ProviderType.askEachTime, ProviderType.onramper];
55 + case WalletType.monero:
56 + return [ProviderType.askEachTime, ProviderType.onramper, ProviderType.dfx];
57 + case WalletType.bitcoin:
58 + case WalletType.ethereum:
59 + return [
60 + ProviderType.askEachTime,
61 + ProviderType.onramper,
62 + ProviderType.dfx,
63 + ProviderType.robinhood,
64 + ];
65 + case WalletType.litecoin:
66 + case WalletType.bitcoinCash:
67 + return [ProviderType.askEachTime, ProviderType.onramper, ProviderType.robinhood];
68 + case WalletType.none:
69 + case WalletType.haven:
70 + case WalletType.polygon:
71 + return [];
72 + }
73 + }
74 +
75 + static List<ProviderType> getAvailableSellProviderTypes(WalletType walletType) {
76 + switch (walletType) {
77 + case WalletType.monero:
78 + return [ProviderType.askEachTime, ProviderType.dfx];
79 + case WalletType.bitcoin:
80 + case WalletType.ethereum:
81 + return [ProviderType.askEachTime, ProviderType.moonpaySell, ProviderType.dfx];
82 + case WalletType.litecoin:
83 + case WalletType.bitcoinCash:
84 + return [ProviderType.askEachTime, ProviderType.moonpaySell];
85 + case WalletType.nano:
86 + case WalletType.banano:
87 + case WalletType.none:
88 + case WalletType.haven:
89 + case WalletType.polygon:
90 + return [];
91 + }
92 + }
93 +
94 + static BuyProvider? getProviderByType(ProviderType type) {
95 + switch (type) {
96 + case ProviderType.robinhood:
97 + return getIt.get<RobinhoodBuyProvider>();
98 + case ProviderType.dfx:
99 + return getIt.get<DFXBuyProvider>();
100 + case ProviderType.onramper:
101 + return getIt.get<OnRamperBuyProvider>();
102 + case ProviderType.askEachTime:
103 + return null;
104 + case ProviderType.moonpaySell:
105 + return getIt.get<MoonPaySellProvider>();
106 + }
107 + }
108 +}
lib/src/screens/buy/buy_options_page.dart
+15 -23
@@ -1,5 +1,3 @@
1 -import 'package:cake_wallet/buy/buy_provider.dart';
2 -import 'package:cake_wallet/entities/buy_provider_types.dart';
1 import 'package:cake_wallet/generated/i18n.dart';
2 import 'package:cake_wallet/src/screens/base_page.dart';
3 import 'package:cake_wallet/src/widgets/option_tile.dart';
@@ -22,8 +20,7 @@ class BuySellOptionsPage extends BasePage {
20
21 @override
22 Widget body(BuildContext context) {
25 - final isLightMode =
26 - Theme.of(context).extension<OptionTileTheme>()?.useDarkImage ?? false;
23 + final isLightMode = Theme.of(context).extension<OptionTileTheme>()?.useDarkImage ?? false;
24 final availableProviders = isBuyAction
25 ? dashboardViewModel.availableBuyProviders
26 : dashboardViewModel.availableSellProviders;
@@ -46,31 +43,26 @@ class BuySellOptionsPage extends BasePage {
43 child: OptionTile(
44 image: icon,
45 title: provider.toString(),
49 - description: isBuyAction
50 - ? provider.buyOptionDescription
51 - : provider.sellOptionDescription,
52 - onPressed: () =>
53 - provider.launchProvider(context, isBuyAction),
46 + description: provider.providerDescription,
47 + onPressed: () => provider.launchProvider(context, isBuyAction),
48 ),
49 );
50 }).toList(),
51 Spacer(),
58 - Padding(
59 - padding: EdgeInsets.fromLTRB(24, 24, 24, 32),
60 - child: Text(
61 - isBuyAction
62 - ? S.of(context).select_buy_provider_notice
63 - : S.of(context).select_sell_provider_notice,
64 - textAlign: TextAlign.center,
65 - style: TextStyle(
66 - fontSize: 14,
67 - fontWeight: FontWeight.normal,
68 - color: Theme.of(context)
69 - .extension<TransactionTradeTheme>()!
70 - .detailsTitlesColor,
71 - ),
52 + Padding(
53 + padding: EdgeInsets.fromLTRB(24, 24, 24, 32),
54 + child: Text(
55 + isBuyAction
56 + ? S.of(context).select_buy_provider_notice
57 + : S.of(context).select_sell_provider_notice,
58 + textAlign: TextAlign.center,
59 + style: TextStyle(
60 + fontSize: 14,
61 + fontWeight: FontWeight.normal,
62 + color: Theme.of(context).extension<TransactionTradeTheme>()!.detailsTitlesColor,
63 ),
64 ),
65 + ),
66 ],
67 ),
68 ),
lib/src/screens/buy/buy_webview_page.dart
+1 -1
@@ -1,7 +1,7 @@
1 import 'dart:async';
2 import 'dart:io';
3 import 'package:cake_wallet/buy/buy_provider.dart';
4 -import 'package:cake_wallet/buy/moonpay/moonpay_buy_provider.dart';
4 +import 'package:cake_wallet/buy/moonpay/moonpay_provider.dart';
5 import 'package:cake_wallet/buy/wyre/wyre_buy_provider.dart';
6 import 'package:cake_wallet/generated/i18n.dart';
7 import 'package:cake_wallet/palette.dart';
lib/src/screens/buy/widgets/buy_list_item.dart
+1 -1
@@ -29,7 +29,7 @@ class BuyListItem extends StatelessWidget {
29
30 @override
31 Widget build(BuildContext context) {
32 - final isSelected = selectedProvider?.buyOptionDescription == provider.buyOptionDescription;
32 + final isSelected = selectedProvider?.providerDescription == provider.providerDescription;
33 final iconColor = isSelected ? Colors.white : Colors.black;
34
35 final providerIcon = Image.asset('assets/images/wyre-icon.png', width: 36, height: 36);
lib/src/screens/exchange_trade/exchange_trade_page.dart
+4 -2
@@ -299,7 +299,8 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
299 }
300
301 void transactionStatePopup() {
302 - showPopUp<void>(
302 + if (this.mounted) {
303 + showPopUp<void>(
304 context: context,
305 builder: (BuildContext popupContext) {
306 return Observer(builder: (_) {
@@ -344,7 +345,7 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
345 onPressed: () {
346 Navigator.of(popupContext).pop();
347 RequestReviewHandler.requestReview();
347 - },
348 + },
349 text: S.of(popupContext).got_it,
350 color: Theme.of(popupContext).primaryColor,
351 textColor: Colors.white))
@@ -391,5 +392,6 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
392 );
393 });
394 });
395 + }
396 }
397 }
lib/src/screens/wallet_list/wallet_list_page.dart
+3 -2
@@ -1,6 +1,5 @@
1 import 'package:cake_wallet/entities/wallet_list_order_types.dart';
2 import 'package:cake_wallet/src/screens/dashboard/widgets/filter_list_widget.dart';
3 -import 'package:cake_wallet/src/screens/dashboard/widgets/filter_widget.dart';
3 import 'package:cake_wallet/src/screens/wallet_list/filtered_list.dart';
4 import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
5 import 'package:cake_wallet/core/auth_service.dart';
@@ -335,7 +334,9 @@ class WalletListBodyState extends State<WalletListBody> {
334 // in desktop platforms the navigation tree is different
335 if (responsiveLayoutUtil.shouldRenderMobileUI) {
336 WidgetsBinding.instance.addPostFrameCallback((_) {
338 - Navigator.of(context).pop();
337 + if (this.mounted) {
338 + Navigator.of(context).pop();
339 + }
340 });
341 }
342 } catch (e) {
lib/store/settings_store.dart
+11 -11
@@ -4,7 +4,7 @@ import 'package:cake_wallet/bitcoin/bitcoin.dart';
4 import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
5 import 'package:cake_wallet/buy/buy_provider.dart';
6 import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
7 -import 'package:cake_wallet/entities/buy_provider_types.dart';
7 +import 'package:cake_wallet/entities/provider_types.dart';
8 import 'package:cake_wallet/entities/cake_2fa_preset_options.dart';
9 import 'package:cake_wallet/entities/background_tasks.dart';
10 import 'package:cake_wallet/entities/exchange_api_mode.dart';
@@ -149,8 +149,8 @@ abstract class SettingsStoreBase with Store {
149 currentSyncMode = initialSyncMode,
150 currentSyncAll = initialSyncAll,
151 priority = ObservableMap<WalletType, TransactionPriority>(),
152 - defaultBuyProviders = ObservableMap<WalletType, BuyProviderType>(),
153 - defaultSellProviders = ObservableMap<WalletType, BuyProviderType>() {
152 + defaultBuyProviders = ObservableMap<WalletType, ProviderType>(),
153 + defaultSellProviders = ObservableMap<WalletType, ProviderType>() {
154 //this.nodes = ObservableMap<WalletType, Node>.of(nodes);
155
156 if (initialMoneroTransactionPriority != null) {
@@ -187,10 +187,10 @@ abstract class SettingsStoreBase with Store {
187 final key = 'buyProvider_${walletType.toString()}';
188 final providerId = sharedPreferences.getString(key);
189 if (providerId != null) {
190 - defaultBuyProviders[walletType] = BuyProviderType.values
191 - .firstWhere((provider) => provider.id == providerId, orElse: () => BuyProviderType.askEachTime);
190 + defaultBuyProviders[walletType] = ProviderType.values
191 + .firstWhere((provider) => provider.id == providerId, orElse: () => ProviderType.askEachTime);
192 } else {
193 - defaultBuyProviders[walletType] = BuyProviderType.askEachTime;
193 + defaultBuyProviders[walletType] = ProviderType.askEachTime;
194 }
195 });
196
@@ -198,10 +198,10 @@ abstract class SettingsStoreBase with Store {
198 final key = 'sellProvider_${walletType.toString()}';
199 final providerId = sharedPreferences.getString(key);
200 if (providerId != null) {
201 - defaultSellProviders[walletType] = BuyProviderType.values
202 - .firstWhere((provider) => provider.id == providerId, orElse: () => BuyProviderType.askEachTime);
201 + defaultSellProviders[walletType] = ProviderType.values
202 + .firstWhere((provider) => provider.id == providerId, orElse: () => ProviderType.askEachTime);
203 } else {
204 - defaultSellProviders[walletType] = BuyProviderType.askEachTime;
204 + defaultSellProviders[walletType] = ProviderType.askEachTime;
205 }
206 });
207
@@ -617,10 +617,10 @@ abstract class SettingsStoreBase with Store {
617 ObservableMap<String, bool> trocadorProviderStates = ObservableMap<String, bool>();
618
619 @observable
620 - ObservableMap<WalletType, BuyProviderType> defaultBuyProviders;
620 + ObservableMap<WalletType, ProviderType> defaultBuyProviders;
621
622 @observable
623 - ObservableMap<WalletType, BuyProviderType> defaultSellProviders;
623 + ObservableMap<WalletType, ProviderType> defaultSellProviders;
624
625 @observable
626 SortBalanceBy sortBalanceBy;
lib/view_model/buy/buy_view_model.dart
+1 -1
@@ -1,5 +1,5 @@
1 import 'package:cake_wallet/buy/buy_provider.dart';
2 -import 'package:cake_wallet/buy/moonpay/moonpay_buy_provider.dart';
2 +import 'package:cake_wallet/buy/moonpay/moonpay_provider.dart';
3 import 'package:cake_wallet/buy/wyre/wyre_buy_provider.dart';
4 import 'package:cw_core/crypto_currency.dart';
5 import 'package:cake_wallet/entities/fiat_currency.dart';
lib/view_model/dashboard/dashboard_view_model.dart
+10 -10
@@ -4,7 +4,7 @@ import 'package:cake_wallet/buy/buy_provider.dart';
4 import 'package:cake_wallet/core/key_service.dart';
5 import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
6 import 'package:cake_wallet/entities/balance_display_mode.dart';
7 -import 'package:cake_wallet/entities/buy_provider_types.dart';
7 +import 'package:cake_wallet/entities/provider_types.dart';
8 import 'package:cake_wallet/entities/exchange_api_mode.dart';
9 import 'package:cake_wallet/exchange/exchange_provider_description.dart';
10 import 'package:cake_wallet/generated/i18n.dart';
@@ -42,7 +42,7 @@ import 'package:cw_core/wallet_type.dart';
42 import 'package:eth_sig_util/util/utils.dart';
43 import 'package:flutter/services.dart';
44 import 'package:mobx/mobx.dart';
45 -import 'package:cake_wallet/entities/buy_provider_types.dart';
45 +import 'package:cake_wallet/entities/provider_types.dart';
46
47 part 'dashboard_view_model.g.dart';
48
@@ -297,27 +297,27 @@ abstract class DashboardViewModelBase with Store {
297
298 Map<String, List<FilterItem>> filterItems;
299
300 - BuyProvider? get defaultBuyProvider => BuyProviderHelper.getProviderByType(
301 - settingsStore.defaultBuyProviders[wallet.type] ?? BuyProviderType.askEachTime);
300 + BuyProvider? get defaultBuyProvider => ProvidersHelper.getProviderByType(
301 + settingsStore.defaultBuyProviders[wallet.type] ?? ProviderType.askEachTime);
302
303 - BuyProvider? get defaultSellProvider => BuyProviderHelper.getProviderByType(
304 - settingsStore.defaultSellProviders[wallet.type] ?? BuyProviderType.askEachTime);
303 + BuyProvider? get defaultSellProvider => ProvidersHelper.getProviderByType(
304 + settingsStore.defaultSellProviders[wallet.type] ?? ProviderType.askEachTime);
305
306 bool get isBuyEnabled => settingsStore.isBitcoinBuyEnabled;
307
308 List<BuyProvider> get availableBuyProviders {
309 - final providerTypes = BuyProviderHelper.getAvailableBuyProviderTypes(wallet.type);
309 + final providerTypes = ProvidersHelper.getAvailableBuyProviderTypes(wallet.type);
310 return providerTypes
311 - .map((type) => BuyProviderHelper.getProviderByType(type))
311 + .map((type) => ProvidersHelper.getProviderByType(type))
312 .where((provider) => provider != null)
313 .cast<BuyProvider>()
314 .toList();
315 }
316
317 List<BuyProvider> get availableSellProviders {
318 - final providerTypes = BuyProviderHelper.getAvailableSellProviderTypes(wallet.type);
318 + final providerTypes = ProvidersHelper.getAvailableSellProviderTypes(wallet.type);
319 return providerTypes
320 - .map((type) => BuyProviderHelper.getProviderByType(type))
320 + .map((type) => ProvidersHelper.getProviderByType(type))
321 .where((provider) => provider != null)
322 .cast<BuyProvider>()
323 .toList();
lib/view_model/order_details_view_model.dart
+1 -1
@@ -9,7 +9,7 @@ import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.d
9 import 'package:cake_wallet/src/screens/trade_details/track_trade_list_item.dart';
10 import 'package:url_launcher/url_launcher.dart';
11 import 'package:cw_core/wallet_base.dart';
12 -import 'package:cake_wallet/buy/moonpay/moonpay_buy_provider.dart';
12 +import 'package:cake_wallet/buy/moonpay/moonpay_provider.dart';
13 import 'package:cake_wallet/buy/wyre/wyre_buy_provider.dart';
14
15 part 'order_details_view_model.g.dart';
lib/view_model/settings/other_settings_view_model.dart
+18 -18
@@ -1,5 +1,5 @@
1 import 'package:cake_wallet/bitcoin/bitcoin.dart';
2 -import 'package:cake_wallet/entities/buy_provider_types.dart';
2 +import 'package:cake_wallet/entities/provider_types.dart';
3 import 'package:cake_wallet/entities/priority_for_wallet_type.dart';
4 import 'package:cake_wallet/generated/i18n.dart';
5 import 'package:cake_wallet/store/settings_store.dart';
@@ -64,20 +64,20 @@ abstract class OtherSettingsViewModelBase with Store {
64 bool get isEnabledSellAction =>
65 !_settingsStore.disableSell && _wallet.type != WalletType.haven;
66
67 - List<BuyProviderType> get availableBuyProvidersTypes {
68 - return BuyProviderHelper.getAvailableBuyProviderTypes(walletType);
67 + List<ProviderType> get availableBuyProvidersTypes {
68 + return ProvidersHelper.getAvailableBuyProviderTypes(walletType);
69 }
70
71 - List<BuyProviderType> get availableSellProvidersTypes =>
72 - BuyProviderHelper.getAvailableSellProviderTypes(walletType);
71 + List<ProviderType> get availableSellProvidersTypes =>
72 + ProvidersHelper.getAvailableSellProviderTypes(walletType);
73
74 - BuyProviderType get buyProviderType =>
74 + ProviderType get buyProviderType =>
75 _settingsStore.defaultBuyProviders[walletType] ??
76 - BuyProviderType.askEachTime;
76 + ProviderType.askEachTime;
77
78 - BuyProviderType get sellProviderType =>
78 + ProviderType get sellProviderType =>
79 _settingsStore.defaultSellProviders[walletType] ??
80 - BuyProviderType.askEachTime;
80 + ProviderType.askEachTime;
81
82 String getDisplayPriority(dynamic priority) {
83 final _priority = priority as TransactionPriority;
@@ -93,28 +93,28 @@ abstract class OtherSettingsViewModelBase with Store {
93 }
94
95 String getBuyProviderType(dynamic buyProviderType) {
96 - final _buyProviderType = buyProviderType as BuyProviderType;
97 - return _buyProviderType == BuyProviderType.askEachTime
96 + final _buyProviderType = buyProviderType as ProviderType;
97 + return _buyProviderType == ProviderType.askEachTime
98 ? S.current.ask_each_time
99 - : _buyProviderType.name;
99 + : _buyProviderType.title;
100 }
101
102 String getSellProviderType(dynamic sellProviderType) {
103 - final _sellProviderType = sellProviderType as BuyProviderType;
104 - return _sellProviderType == BuyProviderType.askEachTime
103 + final _sellProviderType = sellProviderType as ProviderType;
104 + return _sellProviderType == ProviderType.askEachTime
105 ? S.current.ask_each_time
106 - : _sellProviderType.name;
106 + : _sellProviderType.title;
107 }
108
109 void onDisplayPrioritySelected(TransactionPriority priority) =>
110 _settingsStore.priority[_wallet.type] = priority;
111
112 @action
113 - BuyProviderType onBuyProviderTypeSelected(BuyProviderType buyProviderType) =>
113 + ProviderType onBuyProviderTypeSelected(ProviderType buyProviderType) =>
114 _settingsStore.defaultBuyProviders[walletType] = buyProviderType;
115
116 @action
117 - BuyProviderType onSellProviderTypeSelected(
118 - BuyProviderType sellProviderType) =>
117 + ProviderType onSellProviderTypeSelected(
118 + ProviderType sellProviderType) =>
119 _settingsStore.defaultSellProviders[walletType] = sellProviderType;
120 }
lib/view_model/wallet_list/wallet_list_view_model.dart
+1
@@ -22,6 +22,7 @@ abstract class WalletListViewModelBase with Store {
22 ) : wallets = ObservableList<WalletListItem>() {
23 setOrderType(_appStore.settingsStore.walletListOrder);
24 reaction((_) => _appStore.wallet, (_) => updateList());
25 + updateList();
26 }
27
28 @observable