Merge branch 'main' into CW-529-Modify-2FA-introduction-screens

This commit is contained in:
Serhii
2023-12-30 16:58:46 +02:00
76 changed files with 9627 additions and 492 deletions
+4 -4
View File
@@ -2,11 +2,11 @@ import 'package:flutter/foundation.dart';
import 'package:cake_wallet/buy/buy_provider_description.dart';
class BuyException implements Exception {
BuyException({required this.description, required this.text});
BuyException({required this.title, required this.content});
final BuyProviderDescription description;
final String text;
final String title;
final String content;
@override
String toString() => '${description.title}: $text';
String toString() => '$title: $content';
}
+20 -12
View File
@@ -1,27 +1,35 @@
import 'package:cake_wallet/buy/buy_amount.dart';
import 'package:cake_wallet/buy/buy_provider_description.dart';
import 'package:cake_wallet/buy/order.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
abstract class BuyProvider {
BuyProvider({required this.wallet, required this.isTestEnvironment});
BuyProvider({
required this.wallet,
required this.isTestEnvironment,
});
final WalletBase wallet;
final bool isTestEnvironment;
String get title;
BuyProviderDescription get description;
String get trackUrl;
WalletType get walletType => wallet.type;
String get walletAddress => wallet.walletAddresses.address;
String get walletId => wallet.id;
String get buyOptionDescription;
String get sellOptionDescription;
String get lightIcon;
String get darkIcon;
@override
String toString() => title;
Future<String> requestUrl(String amount, String sourceCurrency);
Future<Order> findOrderById(String id);
Future<BuyAmount> calculateAmount(String amount, String sourceCurrency);
}
Future<void> launchProvider(BuildContext context, bool? isBuyAction);
Future<String> requestUrl(String amount, String sourceCurrency) => throw UnimplementedError();
Future<Order> findOrderById(String id) => throw UnimplementedError();
Future<BuyAmount> calculateAmount(String amount, String sourceCurrency) => throw UnimplementedError();
}
+44 -20
View File
@@ -1,5 +1,6 @@
import 'dart:convert';
import 'package:cake_wallet/buy/buy_provider.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
@@ -11,10 +12,9 @@ import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:url_launcher/url_launcher.dart';
class DFXBuyProvider {
DFXBuyProvider({required WalletBase wallet}) : this._wallet = wallet;
final WalletBase _wallet;
class DFXBuyProvider extends BuyProvider {
DFXBuyProvider({required WalletBase wallet, bool isTestEnvironment = false})
: super(wallet: wallet, isTestEnvironment: isTestEnvironment);
static const _baseUrl = 'api.dfx.swiss';
static const _authPath = '/v1/auth/signMessage';
@@ -22,8 +22,23 @@ class DFXBuyProvider {
static const _signInPath = '/v1/auth/signIn';
static const walletName = 'CakeWallet';
@override
String get title => 'DFX Connect';
@override
String get buyOptionDescription => S.current.dfx_option_description;
@override
String get sellOptionDescription => S.current.dfx_option_description;
@override
String get lightIcon => 'assets/images/dfx_light.png';
@override
String get darkIcon => 'assets/images/dfx_dark.png';
String get assetOut {
switch (_wallet.type) {
switch (wallet.type) {
case WalletType.bitcoin:
return 'BTC';
case WalletType.bitcoinCash:
@@ -35,12 +50,12 @@ class DFXBuyProvider {
case WalletType.ethereum:
return 'ETH';
default:
throw Exception("WalletType is not available for DFX ${_wallet.type}");
throw Exception("WalletType is not available for DFX ${wallet.type}");
}
}
String get blockchain {
switch (_wallet.type) {
switch (wallet.type) {
case WalletType.bitcoin:
case WalletType.bitcoinCash:
case WalletType.litecoin:
@@ -50,12 +65,12 @@ class DFXBuyProvider {
case WalletType.ethereum:
return 'Ethereum';
default:
throw Exception("WalletType is not available for DFX ${_wallet.type}");
throw Exception("WalletType is not available for DFX ${wallet.type}");
}
}
Future<String> getSignMessage() async {
final walletAddress = _wallet.walletAddresses.address;
final walletAddress = wallet.walletAddresses.address;
final uri = Uri.https(_baseUrl, _authPath, {'address': walletAddress});
var response = await http.get(uri, headers: {'accept': 'application/json'});
@@ -71,7 +86,7 @@ class DFXBuyProvider {
Future<String> signUp() async {
final signMessage = getSignature(await getSignMessage());
final walletAddress = _wallet.walletAddresses.address;
final walletAddress = wallet.walletAddresses.address;
final requestBody = jsonEncode({
'wallet': walletName,
@@ -86,6 +101,10 @@ class DFXBuyProvider {
if (response.statusCode == 201) {
final responseBody = jsonDecode(response.body);
return responseBody['accessToken'] as String;
} else if (response.statusCode == 403) {
final responseBody = jsonDecode(response.body);
final message = responseBody['message'] ?? 'Service unavailable in your country';
throw Exception(message);
} else {
throw Exception(
'Failed to sign up. Status: ${response.statusCode} ${response.body}');
@@ -94,7 +113,7 @@ class DFXBuyProvider {
Future<String> signIn() async {
final signMessage = getSignature(await getSignMessage());
final walletAddress = _wallet.walletAddresses.address;
final walletAddress = wallet.walletAddresses.address;
final requestBody = jsonEncode({
'address': walletAddress,
@@ -108,6 +127,10 @@ class DFXBuyProvider {
if (response.statusCode == 201) {
final responseBody = jsonDecode(response.body);
return responseBody['accessToken'] as String;
} else if (response.statusCode == 403) {
final responseBody = jsonDecode(response.body);
final message = responseBody['message'] ?? 'Service unavailable in your country';
throw Exception(message);
} else {
throw Exception(
'Failed to sign in. Status: ${response.statusCode} ${response.body}');
@@ -115,24 +138,25 @@ class DFXBuyProvider {
}
String getSignature(String message) {
switch (_wallet.type) {
switch (wallet.type) {
case WalletType.ethereum:
return _wallet.signMessage(message);
return wallet.signMessage(message);
case WalletType.monero:
case WalletType.litecoin:
case WalletType.bitcoin:
case WalletType.bitcoinCash:
return _wallet.signMessage(message,
address: _wallet.walletAddresses.address);
return wallet.signMessage(message,
address: wallet.walletAddresses.address);
default:
throw Exception("WalletType is not available for DFX ${_wallet.type}");
throw Exception("WalletType is not available for DFX ${wallet.type}");
}
}
Future<void> launchProvider(BuildContext context) async {
Future<void> launchProvider(BuildContext context, bool? isBuyAction) async {
try {
final assetOut = this.assetOut;
final blockchain = this.blockchain;
final actionType = isBuyAction == true ? '/buy' : '/sell';
String accessToken;
@@ -146,7 +170,7 @@ class DFXBuyProvider {
}
}
final uri = Uri.https('services.dfx.swiss', '/buy', {
final uri = Uri.https('services.dfx.swiss', actionType, {
'session': accessToken,
'lang': 'en',
'asset-out': assetOut,
@@ -156,8 +180,8 @@ class DFXBuyProvider {
if (await canLaunchUrl(uri)) {
if (DeviceInfo.instance.isMobile) {
Navigator.of(context).pushNamed(Routes.webViewPage,
arguments: [S.of(context).buy, uri]);
Navigator.of(context)
.pushNamed(Routes.webViewPage, arguments: ["DFX Connect", uri]);
} else {
await launchUrl(uri, mode: LaunchMode.externalApplication);
}
+89 -28
View File
@@ -1,9 +1,14 @@
import 'dart:convert';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:crypto/crypto.dart';
import 'package:cake_wallet/buy/buy_exception.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart';
import 'package:cake_wallet/buy/buy_amount.dart';
import 'package:cake_wallet/buy/buy_provider.dart';
@@ -14,33 +19,41 @@ import 'package:cw_core/wallet_type.dart';
import 'package:cake_wallet/exchange/trade_state.dart';
import 'package:cake_wallet/.secrets.g.dart' as secrets;
import 'package:cw_core/crypto_currency.dart';
import 'package:url_launcher/url_launcher.dart';
class MoonPaySellProvider {
MoonPaySellProvider({this.isTest = false})
: baseUrl = isTest ? _baseTestUrl : _baseProductUrl;
MoonPaySellProvider({required SettingsStore settingsStore,
required WalletBase wallet, this.isTest = false})
: baseUrl = isTest ? _baseTestUrl : _baseProductUrl,
this._settingsStore = settingsStore,
this._wallet = wallet;
final SettingsStore _settingsStore;
final WalletBase _wallet;
static const _baseTestUrl = 'sell-sandbox.moonpay.com';
static const _baseProductUrl = 'sell.moonpay.com';
static String themeToMoonPayTheme(ThemeBase theme) {
switch (theme.type) {
case ThemeType.bright:
return 'light';
case ThemeType.light:
return 'light';
case ThemeType.dark:
return 'dark';
}
}
static String get _apiKey => secrets.moonPayApiKey;
static String get _secretKey => secrets.moonPaySecretKey;
static String get _apiKey => secrets.moonPayApiKey;
static String get _secretKey => secrets.moonPaySecretKey;
final bool isTest;
final String baseUrl;
Future<Uri> requestUrl(
{required CryptoCurrency currency,
required String refundWalletAddress,
required SettingsStore settingsStore}) async {
Future<Uri> requestUrl({
required CryptoCurrency currency,
required String refundWalletAddress,
required SettingsStore settingsStore,
}) async {
final customParams = {
'theme': themeToMoonPayTheme(settingsStore.currentTheme),
'language': settingsStore.languageCode,
@@ -50,11 +63,15 @@ class MoonPaySellProvider {
};
final originalUri = Uri.https(
baseUrl, '', <String, dynamic>{
baseUrl,
'',
<String, dynamic>{
'apiKey': _apiKey,
'defaultBaseCurrencyCode': currency.toString().toLowerCase(),
'refundWalletAddress': refundWalletAddress
}..addAll(customParams));
'refundWalletAddress': refundWalletAddress,
}..addAll(customParams),
);
final messageBytes = utf8.encode('?${originalUri.query}');
final key = utf8.encode(_secretKey);
final hmac = Hmac(sha256, key);
@@ -70,6 +87,38 @@ class MoonPaySellProvider {
final signedUri = originalUri.replace(queryParameters: query);
return signedUri;
}
Future<void> launchProvider(BuildContext context) async {
try {
final uri = await requestUrl(
currency: _wallet.currency,
refundWalletAddress: _wallet.walletAddresses.address,
settingsStore: _settingsStore,
);
if (await canLaunchUrl(uri)) {
if (DeviceInfo.instance.isMobile) {
Navigator.of(context).pushNamed(Routes.webViewPage, arguments: ['MoonPay', uri]);
} else {
await launchUrl(uri, mode: LaunchMode.externalApplication);
}
} else {
throw Exception('Could not launch URL');
}
} catch (e) {
await showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: 'MoonPay',
alertContent: 'The MoonPay service is currently unavailable: $e',
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop(),
);
},
);
}
}
}
class MoonPayBuyProvider extends BuyProvider {
@@ -88,20 +137,24 @@ class MoonPayBuyProvider extends BuyProvider {
static const _secretKey = secrets.moonPaySecretKey;
@override
String get title => 'MoonPay';
String get title => 'Moon Pay';
@override
BuyProviderDescription get description => BuyProviderDescription.moonPay;
String get buyOptionDescription => '';
@override
String get lightIcon => 'assets/images/moonpay_light.png';
@override
String get darkIcon => 'assets/images/moonpay_dark.png';
String get currencyCode =>
walletTypeToCryptoCurrency(walletType).title.toLowerCase();
walletTypeToCryptoCurrency(wallet.type).title.toLowerCase();
@override
String get trackUrl => baseUrl + '/transaction_receipt?transactionId=';
String baseUrl;
@override
Future<String> requestUrl(String amount, String sourceCurrency) async {
final enabledPaymentMethods =
'credit_debit_card%2Capple_pay%2Cgoogle_pay%2Csamsung_pay'
@@ -109,7 +162,7 @@ class MoonPayBuyProvider extends BuyProvider {
final suffix = '?apiKey=' + _apiKey + '&currencyCode=' +
currencyCode + '&enabledPaymentMethods=' + enabledPaymentMethods +
'&walletAddress=' + walletAddress +
'&walletAddress=' + wallet.walletAddresses.address +
'&baseCurrencyCode=' + sourceCurrency.toLowerCase() +
'&baseCurrencyAmount=' + amount + '&lockAmount=true' +
'&showAllCurrencies=false' + '&showWalletAddressForm=false';
@@ -127,7 +180,6 @@ class MoonPayBuyProvider extends BuyProvider {
return isTestEnvironment ? originalUrl : urlWithSignature;
}
@override
Future<BuyAmount> calculateAmount(String amount, String sourceCurrency) async {
final url = _apiUrl + _currenciesSuffix + '/$currencyCode' +
_quoteSuffix + '/?apiKey=' + _apiKey +
@@ -138,8 +190,8 @@ class MoonPayBuyProvider extends BuyProvider {
if (response.statusCode != 200) {
throw BuyException(
description: description,
text: 'Quote is not found!');
title: buyOptionDescription,
content: 'Quote is not found!');
}
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
@@ -153,7 +205,6 @@ class MoonPayBuyProvider extends BuyProvider {
minAmount: minSourceAmount);
}
@override
Future<Order> findOrderById(String id) async {
final url = _apiUrl + _transactionsSuffix + '/$id' +
'?apiKey=' + _apiKey;
@@ -162,8 +213,8 @@ class MoonPayBuyProvider extends BuyProvider {
if (response.statusCode != 200) {
throw BuyException(
description: description,
text: 'Transaction $id is not found!');
title: buyOptionDescription,
content: 'Transaction $id is not found!');
}
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
@@ -175,13 +226,13 @@ class MoonPayBuyProvider extends BuyProvider {
return Order(
id: id,
provider: description,
provider: BuyProviderDescription.moonPay,
transferId: id,
state: state,
createdAt: createdAt,
amount: amount.toString(),
receiveAddress: walletAddress,
walletId: walletId
receiveAddress: wallet.walletAddresses.address,
walletId: wallet.id
);
}
@@ -201,4 +252,14 @@ class MoonPayBuyProvider extends BuyProvider {
return isBuyEnable;
}
@override
// TODO: implement sellOptionDescription
String get sellOptionDescription => throw UnimplementedError();
@override
Future<void> launchProvider(BuildContext context, bool? isBuyAction) {
// TODO: implement launchProvider
throw UnimplementedError();
}
}
+36 -18
View File
@@ -1,4 +1,5 @@
import 'package:cake_wallet/.secrets.g.dart' as secrets;
import 'package:cake_wallet/buy/buy_provider.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/store/settings_store.dart';
@@ -9,20 +10,34 @@ import 'package:cw_core/wallet_base.dart';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
class OnRamperBuyProvider {
OnRamperBuyProvider({required SettingsStore settingsStore, required WalletBase wallet})
: this._settingsStore = settingsStore,
this._wallet = wallet;
final SettingsStore _settingsStore;
final WalletBase _wallet;
class OnRamperBuyProvider extends BuyProvider {
OnRamperBuyProvider(this._settingsStore,
{required WalletBase wallet, bool isTestEnvironment = false})
: super(wallet: wallet, isTestEnvironment: isTestEnvironment);
static const _baseUrl = 'buy.onramper.com';
final SettingsStore _settingsStore;
@override
String get title => 'Onramper';
@override
String get buyOptionDescription => S.current.onramper_option_description;
@override
String get sellOptionDescription => S.current.onramper_option_description;
@override
String get lightIcon => 'assets/images/onramper_light.png';
@override
String get darkIcon => 'assets/images/onramper_dark.png';
String get _apiKey => secrets.onramperApiKey;
String get _normalizeCryptoCurrency {
switch (_wallet.currency) {
switch (wallet.currency) {
case CryptoCurrency.ltc:
return "LTC_LITECOIN";
case CryptoCurrency.xmr:
@@ -32,7 +47,7 @@ class OnRamperBuyProvider {
case CryptoCurrency.nano:
return "XNO_NANO";
default:
return _wallet.currency.title;
return wallet.currency.title;
}
}
@@ -40,7 +55,7 @@ class OnRamperBuyProvider {
return color.value.toRadixString(16).replaceAll(RegExp(r'^ff'), "");
}
Uri requestUrl(BuildContext context) {
Uri requestOnramperUrl(BuildContext context) {
String primaryColor,
secondaryColor,
primaryTextColor,
@@ -50,9 +65,10 @@ class OnRamperBuyProvider {
primaryColor = getColorStr(Theme.of(context).primaryColor);
secondaryColor = getColorStr(Theme.of(context).colorScheme.background);
primaryTextColor = getColorStr(Theme.of(context).extension<CakeTextTheme>()!.titleColor);
secondaryTextColor =
getColorStr(Theme.of(context).extension<CakeTextTheme>()!.secondaryTextColor);
primaryTextColor =
getColorStr(Theme.of(context).extension<CakeTextTheme>()!.titleColor);
secondaryTextColor = getColorStr(
Theme.of(context).extension<CakeTextTheme>()!.secondaryTextColor);
containerColor = getColorStr(Theme.of(context).colorScheme.background);
cardColor = getColorStr(Theme.of(context).cardColor);
@@ -60,12 +76,13 @@ class OnRamperBuyProvider {
cardColor = getColorStr(Colors.white);
}
final networkName = _wallet.currency.fullName?.toUpperCase().replaceAll(" ", "");
final networkName =
wallet.currency.fullName?.toUpperCase().replaceAll(" ", "");
return Uri.https(_baseUrl, '', <String, dynamic>{
'apiKey': _apiKey,
'defaultCrypto': _normalizeCryptoCurrency,
'networkWallets': '${networkName}:${_wallet.walletAddresses.address}',
'networkWallets': '${networkName}:${wallet.walletAddresses.address}',
'supportSell': "false",
'supportSwap': "false",
'primaryColor': primaryColor,
@@ -77,10 +94,11 @@ class OnRamperBuyProvider {
});
}
Future<void> launchProvider(BuildContext context) async {
final uri = requestUrl(context);
Future<void> launchProvider(BuildContext context, bool? isBuyAction) async {
final uri = requestOnramperUrl(context);
if (DeviceInfo.instance.isMobile) {
Navigator.of(context).pushNamed(Routes.webViewPage, arguments: [S.of(context).buy, uri]);
Navigator.of(context)
.pushNamed(Routes.webViewPage, arguments: [S.of(context).buy, uri]);
} else {
await launchUrl(uri);
}
+35 -22
View File
@@ -1,6 +1,10 @@
import 'dart:convert';
import 'package:cake_wallet/.secrets.g.dart' as secrets;
import 'package:cake_wallet/buy/buy_amount.dart';
import 'package:cake_wallet/buy/buy_provider.dart';
import 'package:cake_wallet/buy/buy_provider_description.dart';
import 'package:cake_wallet/buy/order.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
@@ -10,40 +14,48 @@ import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:url_launcher/url_launcher.dart';
class RobinhoodBuyProvider {
RobinhoodBuyProvider({required WalletBase wallet}) : this._wallet = wallet;
final WalletBase _wallet;
class RobinhoodBuyProvider extends BuyProvider{
RobinhoodBuyProvider({required WalletBase wallet, bool isTestEnvironment = false})
: super(wallet: wallet, isTestEnvironment: isTestEnvironment);
static const _baseUrl = 'applink.robinhood.com';
static const _cIdBaseUrl = 'exchange-helper.cakewallet.com';
@override
String get title => 'Robinhood Connect';
@override
String get buyOptionDescription => S.current.robinhood_option_description;
@override
String get sellOptionDescription => S.current.robinhood_option_description;
@override
String get lightIcon => 'assets/images/robinhood_light.png';
@override
String get darkIcon => 'assets/images/robinhood_dark.png';
String get _applicationId => secrets.robinhoodApplicationId;
String get _apiSecret => secrets.robinhoodCIdApiSecret;
bool get isAvailable => [
WalletType.bitcoin,
WalletType.bitcoinCash,
WalletType.litecoin,
WalletType.ethereum
].contains(_wallet.type);
String getSignature(String message) {
switch (_wallet.type) {
switch (wallet.type) {
case WalletType.ethereum:
return _wallet.signMessage(message);
return wallet.signMessage(message);
case WalletType.litecoin:
case WalletType.bitcoin:
case WalletType.bitcoinCash:
return _wallet.signMessage(message, address: _wallet.walletAddresses.address);
return wallet.signMessage(message, address: wallet.walletAddresses.address);
default:
throw Exception("WalletType is not available for Robinhood ${_wallet.type}");
throw Exception("WalletType is not available for Robinhood ${wallet.type}");
}
}
Future<String> getConnectId() async {
final walletAddress = _wallet.walletAddresses.address;
final walletAddress = wallet.walletAddresses.address;
final valid_until = (DateTime.now().millisecondsSinceEpoch / 1000).round() + 10;
final message = "$_apiSecret:${valid_until}";
@@ -64,22 +76,22 @@ class RobinhoodBuyProvider {
}
}
Future<Uri> requestUrl() async {
Future<Uri> requestProviderUrl() async {
final connectId = await getConnectId();
final networkName = _wallet.currency.fullName?.toUpperCase().replaceAll(" ", "_");
final networkName = wallet.currency.fullName?.toUpperCase().replaceAll(" ", "_");
return Uri.https(_baseUrl, '/u/connect', <String, dynamic>{
'applicationId': _applicationId,
'connectId': connectId,
'walletAddress': _wallet.walletAddresses.address,
'userIdentifier': _wallet.walletAddresses.address,
'walletAddress': wallet.walletAddresses.address,
'userIdentifier': wallet.walletAddresses.address,
'supportedNetworks': networkName
});
}
Future<void> launchProvider(BuildContext context) async {
Future<void> launchProvider(BuildContext context, bool? isBuyAction) async {
try {
final uri = await requestUrl();
final uri = await requestProviderUrl();
await launchUrl(uri, mode: LaunchMode.externalApplication);
} catch (_) {
await showPopUp<void>(
@@ -93,4 +105,5 @@ class RobinhoodBuyProvider {
});
}
}
}
+32 -19
View File
@@ -1,5 +1,6 @@
import 'dart:convert';
import 'package:cake_wallet/buy/buy_exception.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:http/http.dart';
import 'package:cake_wallet/buy/buy_amount.dart';
import 'package:cake_wallet/buy/buy_provider.dart';
@@ -35,16 +36,20 @@ class WyreBuyProvider extends BuyProvider {
String get title => 'Wyre';
@override
BuyProviderDescription get description => BuyProviderDescription.wyre;
String get buyOptionDescription => '';
@override
String get lightIcon => 'assets/images/robinhood_light.png';
@override
String get darkIcon => 'assets/images/robinhood_dark.png';
String get trackUrl => isTestEnvironment
? _trackTestUrl
: _trackProductUrl;
String baseApiUrl;
@override
Future<String> requestUrl(String amount, String sourceCurrency) async {
final timestamp = DateTime.now().millisecondsSinceEpoch.toString();
final url = baseApiUrl + _ordersSuffix + _reserveSuffix +
@@ -53,8 +58,8 @@ class WyreBuyProvider extends BuyProvider {
final body = {
'amount': amount,
'sourceCurrency': sourceCurrency,
'destCurrency': walletTypeToCryptoCurrency(walletType).title,
'dest': walletTypeToString(walletType).toLowerCase() + ':' + walletAddress,
'destCurrency': walletTypeToCryptoCurrency(wallet.type).title,
'dest': walletTypeToString(wallet.type).toLowerCase() + ':' + wallet.walletAddresses.address,
'referrerAccountId': _accountId,
'lockFields': ['amount', 'sourceCurrency', 'destCurrency', 'dest']
};
@@ -68,8 +73,8 @@ class WyreBuyProvider extends BuyProvider {
if (response.statusCode != 200) {
throw BuyException(
description: description,
text: 'Url $url is not found!');
title: buyOptionDescription,
content: 'Url $url is not found!');
}
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
@@ -77,14 +82,13 @@ class WyreBuyProvider extends BuyProvider {
return urlFromResponse;
}
@override
Future<BuyAmount> calculateAmount(String amount, String sourceCurrency) async {
final quoteUrl = _baseProductApiUrl + _ordersSuffix + _quoteSuffix;
final body = {
'amount': amount,
'sourceCurrency': sourceCurrency,
'destCurrency': walletTypeToCryptoCurrency(walletType).title,
'dest': walletTypeToString(walletType).toLowerCase() + ':' + walletAddress,
'destCurrency': walletTypeToCryptoCurrency(wallet.type).title,
'dest': walletTypeToString(wallet.type).toLowerCase() + ':' + wallet.walletAddresses.address,
'accountId': _accountId,
'country': _countryCode
};
@@ -99,8 +103,8 @@ class WyreBuyProvider extends BuyProvider {
if (response.statusCode != 200) {
throw BuyException(
description: description,
text: 'Quote is not found!');
title: buyOptionDescription,
content: 'Quote is not found!');
}
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
@@ -111,7 +115,6 @@ class WyreBuyProvider extends BuyProvider {
return BuyAmount(sourceAmount: sourceAmount, destAmount: destAmount, achSourceAmount: achAmount);
}
@override
Future<Order> findOrderById(String id) async {
final orderUrl = baseApiUrl + _ordersSuffix + '/$id';
final orderUri = Uri.parse(orderUrl);
@@ -119,8 +122,8 @@ class WyreBuyProvider extends BuyProvider {
if (orderResponse.statusCode != 200) {
throw BuyException(
description: description,
text: 'Order $id is not found!');
title: buyOptionDescription,
content: 'Order $id is not found!');
}
final orderResponseJSON =
@@ -141,8 +144,8 @@ class WyreBuyProvider extends BuyProvider {
if (transferResponse.statusCode != 200) {
throw BuyException(
description: description,
text: 'Transfer $transferId is not found!');
title: buyOptionDescription,
content: 'Transfer $transferId is not found!');
}
final transferResponseJSON =
@@ -151,15 +154,25 @@ class WyreBuyProvider extends BuyProvider {
return Order(
id: id,
provider: description,
provider: BuyProviderDescription.wyre,
transferId: transferId,
from: from,
to: to,
state: state,
createdAt: createdAt,
amount: amount.toString(),
receiveAddress: walletAddress,
walletId: walletId
receiveAddress: wallet.walletAddresses.address,
walletId: wallet.id
);
}
@override
Future<void> launchProvider(BuildContext context, bool? isBuyAction) {
// TODO: implement launchProvider
throw UnimplementedError();
}
@override
// TODO: implement sellOptionDescription
String get sellOptionDescription => throw UnimplementedError();
}
+1 -1
View File
@@ -92,7 +92,7 @@ class AddressValidator extends TextValidator {
case CryptoCurrency.eos:
return '[0-9a-zA-Z]';
case CryptoCurrency.bch:
return '^(?!bitcoincash:)[0-9a-zA-Z]*\$|^(?!bitcoincash:)q[0-9a-zA-Z]{41}\$|^(?!bitcoincash:)q[0-9a-zA-Z]{42}\$|^bitcoincash:q[0-9a-zA-Z]{41}\$|^bitcoincash:q[0-9a-zA-Z]{42}\$';
return '^(?!bitcoincash:)[0-9a-zA-Z]*\$|^(?!bitcoincash:)q|p[0-9a-zA-Z]{41}\$|^(?!bitcoincash:)q|p[0-9a-zA-Z]{42}\$|^bitcoincash:q|p[0-9a-zA-Z]{41}\$|^bitcoincash:q|p[0-9a-zA-Z]{42}\$';
case CryptoCurrency.bnb:
return '[0-9a-zA-Z]';
case CryptoCurrency.ltc:
@@ -138,7 +138,7 @@ class EvmChainServiceImpl implements ChainService {
try {
// Load the private key
final List<ChainKeyModel> keys = wcKeyService
.getKeysForChain(getChainNameSpaceAndIdBasedOnWalletType(appStore.wallet!.type));
.getKeysForChain(appStore.wallet!);
final Credentials credentials = EthPrivateKey.fromHex(keys[0].privateKey);
@@ -177,7 +177,7 @@ class EvmChainServiceImpl implements ChainService {
try {
// Load the private key
final List<ChainKeyModel> keys = wcKeyService
.getKeysForChain(getChainNameSpaceAndIdBasedOnWalletType(appStore.wallet!.type));
.getKeysForChain(appStore.wallet!);
final EthPrivateKey credentials = EthPrivateKey.fromHex(keys[0].privateKey);
@@ -215,7 +215,7 @@ class EvmChainServiceImpl implements ChainService {
// Load the private key
final List<ChainKeyModel> keys = wcKeyService
.getKeysForChain(getChainNameSpaceAndIdBasedOnWalletType(appStore.wallet!.type));
.getKeysForChain(appStore.wallet!);
final Credentials credentials = EthPrivateKey.fromHex(keys[0].privateKey);
@@ -275,7 +275,7 @@ class EvmChainServiceImpl implements ChainService {
}
final List<ChainKeyModel> keys = wcKeyService
.getKeysForChain(getChainNameSpaceAndIdBasedOnWalletType(appStore.wallet!.type));
.getKeysForChain(appStore.wallet!);
return EthSigUtil.signTypedData(
privateKey: keys[0].privateKey,
@@ -1,48 +1,22 @@
import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/core/wallet_connect/models/chain_key_model.dart';
import 'package:cake_wallet/polygon/polygon.dart';
import 'package:cw_core/balance.dart';
import 'package:cw_core/transaction_history.dart';
import 'package:cw_core/transaction_info.dart';
import 'package:cake_wallet/reactions/wallet_connect.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_type.dart';
abstract class WalletConnectKeyService {
/// Returns a list of all the keys.
List<ChainKeyModel> getKeys();
/// Returns a list of all the chain ids.
List<String> getChains();
List<ChainKeyModel> getKeys(WalletBase wallet);
/// Returns a list of all the keys for a given chain id.
/// If the chain is not found, returns an empty list.
/// - [chain]: The chain to get the keys for.
List<ChainKeyModel> getKeysForChain(String chain);
List<ChainKeyModel> getKeysForChain(WalletBase wallet);
/// Returns a list of all the accounts in namespace:chainId:address format.
List<String> getAllAccounts();
}
class KeyServiceImpl implements WalletConnectKeyService {
KeyServiceImpl(this.wallet)
: _keys = [
ChainKeyModel(
chains: [
'eip155:1',
'eip155:5',
'eip155:137',
'eip155:42161',
'eip155:80001',
],
privateKey: _getPrivateKeyForWallet(wallet),
publicKey: _getPublicKeyForWallet(wallet),
),
];
late final WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo> wallet;
late final List<ChainKeyModel> _keys;
static String _getPrivateKeyForWallet(WalletBase wallet) {
switch (wallet.type) {
case WalletType.ethereum:
@@ -64,31 +38,31 @@ class KeyServiceImpl implements WalletConnectKeyService {
return '';
}
}
@override
List<String> getChains() {
final List<String> chainIds = [];
for (final ChainKeyModel key in _keys) {
chainIds.addAll(key.chains);
}
return chainIds;
List<ChainKeyModel> getKeys(WalletBase wallet) {
final keys = [
ChainKeyModel(
chains: [
'eip155:1',
'eip155:5',
'eip155:137',
'eip155:42161',
'eip155:80001',
],
privateKey: _getPrivateKeyForWallet(wallet),
publicKey: _getPublicKeyForWallet(wallet),
),
];
return keys;
}
@override
List<ChainKeyModel> getKeys() => _keys;
List<ChainKeyModel> getKeysForChain(WalletBase wallet) {
final chain = getChainNameSpaceAndIdBasedOnWalletType(wallet.type);
@override
List<ChainKeyModel> getKeysForChain(String chain) {
return _keys.where((e) => e.chains.contains(chain)).toList();
}
final keys = getKeys(wallet);
@override
List<String> getAllAccounts() {
final List<String> accounts = [];
for (final ChainKeyModel key in _keys) {
for (final String chain in key.chains) {
accounts.add('$chain:${key.publicKey}');
}
}
return accounts;
return keys.where((e) => e.chains.contains(chain)).toList();
}
}
@@ -68,7 +68,7 @@ abstract class Web3WalletServiceBase with Store {
);
// Setup our accounts
List<ChainKeyModel> chainKeys = walletKeyService.getKeys();
List<ChainKeyModel> chainKeys = walletKeyService.getKeys(appStore.wallet!);
for (final chainKey in chainKeys) {
for (final chainId in chainKey.chains) {
_web3Wallet.registerAccount(
@@ -136,6 +136,7 @@ abstract class Web3WalletServiceBase with Store {
_web3Wallet.onAuthRequest.unsubscribe(_onAuthRequest);
_web3Wallet.core.pairing.onPairingDelete.unsubscribe(_onPairingDelete);
_web3Wallet.core.pairing.onPairingExpire.unsubscribe(_onPairingDelete);
isInitialized = false;
}
Web3Wallet getWeb3Wallet() {
@@ -236,7 +237,7 @@ abstract class Web3WalletServiceBase with Store {
Future<void> _onAuthRequest(AuthRequest? args) async {
if (args != null) {
final chaindIdNamespace = getChainNameSpaceAndIdBasedOnWalletType(appStore.wallet!.type);
List<ChainKeyModel> chainKeys = walletKeyService.getKeysForChain(chaindIdNamespace);
List<ChainKeyModel> chainKeys = walletKeyService.getKeysForChain(appStore.wallet!);
// Create the message to be signed
final String iss = 'did:pkh:$chaindIdNamespace:${chainKeys.first.publicKey}';
final Widget modalWidget = Web3RequestModal(
+13 -6
View File
@@ -1,6 +1,7 @@
import 'package:cake_wallet/anonpay/anonpay_api.dart';
import 'package:cake_wallet/anonpay/anonpay_info_base.dart';
import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart';
import 'package:cake_wallet/buy/moonpay/moonpay_buy_provider.dart';
import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart';
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
import 'package:cake_wallet/buy/payfura/payfura_buy_provider.dart';
@@ -378,7 +379,8 @@ Future<void> setup({
settingsStore: settingsStore,
yatStore: getIt.get<YatStore>(),
ordersStore: getIt.get<OrdersStore>(),
anonpayTransactionsStore: getIt.get<AnonpayTransactionsStore>()));
anonpayTransactionsStore: getIt.get<AnonpayTransactionsStore>(),
keyService: getIt.get<KeyService>()));
getIt.registerFactory<AuthService>(
() => AuthService(
@@ -477,7 +479,7 @@ Future<void> setup({
final appStore = getIt.get<AppStore>();
getIt.registerLazySingleton<WalletConnectKeyService>(() => KeyServiceImpl(appStore.wallet!));
getIt.registerLazySingleton<WalletConnectKeyService>(() => KeyServiceImpl());
getIt.registerLazySingleton<Web3WalletService>(() {
final Web3WalletService web3WalletService = Web3WalletService(
@@ -796,11 +798,15 @@ Future<void> setup({
getIt.registerFactory<RobinhoodBuyProvider>(
() => RobinhoodBuyProvider(wallet: getIt.get<AppStore>().wallet!));
getIt.registerFactory<DFXBuyProvider>(
() => DFXBuyProvider(wallet: getIt.get<AppStore>().wallet!));
getIt
.registerFactory<DFXBuyProvider>(() => DFXBuyProvider(wallet: getIt.get<AppStore>().wallet!));
getIt.registerFactory<MoonPaySellProvider>(() => MoonPaySellProvider(
settingsStore: getIt.get<AppStore>().settingsStore,
wallet: getIt.get<AppStore>().wallet!));
getIt.registerFactory<OnRamperBuyProvider>(() => OnRamperBuyProvider(
settingsStore: getIt.get<AppStore>().settingsStore,
getIt.get<AppStore>().settingsStore,
wallet: getIt.get<AppStore>().wallet!,
));
@@ -944,7 +950,8 @@ Future<void> setup({
getIt.registerFactory(() => BuyAmountViewModel());
getIt.registerFactory(() => BuyOptionsPage(getIt.get<DashboardViewModel>()));
getIt.registerFactoryParam<BuySellOptionsPage, bool, void>(
(isBuyOption, _) => BuySellOptionsPage(getIt.get<DashboardViewModel>(), isBuyOption));
getIt.registerFactory(() {
final wallet = getIt.get<AppStore>().wallet;
+87 -32
View File
@@ -1,57 +1,112 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/buy/buy_provider.dart';
import 'package:cake_wallet/buy/dfx/dfx_buy_provider.dart';
import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart';
import 'package:cake_wallet/buy/robinhood/robinhood_buy_provider.dart';
import 'package:cake_wallet/di.dart';
import 'package:cw_core/wallet_type.dart';
enum BuyProviderType {
AskEachTime,
Robinhood,
Onramper,
DFX;
askEachTime,
robinhood,
dfx,
onramper,
}
@override
String toString() {
extension BuyProviderTypeName on BuyProviderType {
String get name {
switch (this) {
case BuyProviderType.AskEachTime:
return S.current.ask_each_time;
case BuyProviderType.Robinhood:
return "Robinhood";
case BuyProviderType.Onramper:
return "Onramper";
case BuyProviderType.DFX:
return "DFX";
case BuyProviderType.askEachTime:
return 'Ask each time';
case BuyProviderType.robinhood:
return 'Robinhood Connect';
case BuyProviderType.dfx:
return 'DFX Connect';
case BuyProviderType.onramper:
return 'Onramper';
default:
return this.toString().split('.').last;
}
}
static List<BuyProviderType> getAvailableProviders(WalletType walletType) {
String get id {
switch (this) {
case BuyProviderType.askEachTime:
return 'ask_each_time_provider';
case BuyProviderType.robinhood:
return 'robinhood_connect_provider';
case BuyProviderType.dfx:
return 'dfx_connect_provider';
case BuyProviderType.onramper:
return 'onramper_provider';
default:
return this.toString().split('.').last.replaceAll('.', '_')
.toLowerCase() + '_provider';
}
}
}
class BuyProviderHelper {
static List<BuyProviderType> getAvailableBuyProviderTypes(
WalletType walletType) {
switch (walletType) {
case WalletType.nano:
case WalletType.banano:
return [
BuyProviderType.AskEachTime,
BuyProviderType.Onramper
];
return [BuyProviderType.askEachTime, BuyProviderType.onramper];
case WalletType.monero:
return [
BuyProviderType.AskEachTime,
BuyProviderType.Onramper,
BuyProviderType.DFX
BuyProviderType.askEachTime,
BuyProviderType.onramper,
BuyProviderType.dfx
];
case WalletType.bitcoin:
case WalletType.ethereum:
return [
BuyProviderType.AskEachTime,
BuyProviderType.Onramper,
BuyProviderType.DFX,
BuyProviderType.Robinhood
BuyProviderType.askEachTime,
BuyProviderType.onramper,
BuyProviderType.dfx,
BuyProviderType.robinhood
];
case WalletType.litecoin:
case WalletType.bitcoinCash:
return [
BuyProviderType.AskEachTime,
BuyProviderType.Onramper,
BuyProviderType.Robinhood
];
return [
BuyProviderType.askEachTime,
BuyProviderType.onramper,
BuyProviderType.robinhood
];
default:
return [];
}
}
static List<BuyProviderType> getAvailableSellProviderTypes(
WalletType walletType) {
switch (walletType) {
case WalletType.nano:
case WalletType.banano:
return [BuyProviderType.askEachTime];
case WalletType.monero:
return [BuyProviderType.askEachTime, BuyProviderType.dfx];
case WalletType.bitcoin:
case WalletType.ethereum:
return [BuyProviderType.askEachTime, BuyProviderType.dfx];
case WalletType.litecoin:
case WalletType.bitcoinCash:
return [BuyProviderType.askEachTime];
default:
return [];
}
}
static BuyProvider? getProviderByType(BuyProviderType type) {
switch (type) {
case BuyProviderType.robinhood:
return getIt.get<RobinhoodBuyProvider>();
case BuyProviderType.dfx:
return getIt.get<DFXBuyProvider>();
case BuyProviderType.onramper:
return getIt.get<OnRamperBuyProvider>();
case BuyProviderType.askEachTime:
return null;
}
}
}
+1 -1
View File
@@ -22,7 +22,7 @@ Future<void> loadCurrentWallet() async {
final type = deserializeFromInt(typeRaw);
final walletLoadingService = getIt.get<WalletLoadingService>();
final wallet = await walletLoadingService.load(type, name);
appStore.changeCurrentWallet(wallet);
await appStore.changeCurrentWallet(wallet);
getIt.get<BackgroundTasks>().registerSyncTask();
}
+34 -81
View File
@@ -1,18 +1,9 @@
import 'package:cake_wallet/buy/dfx/dfx_buy_provider.dart';
import 'package:cake_wallet/buy/moonpay/moonpay_buy_provider.dart';
import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart';
import 'package:cake_wallet/buy/robinhood/robinhood_buy_provider.dart';
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/entities/buy_provider_types.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
class MainActions {
final String Function(BuildContext context) name;
@@ -46,53 +37,22 @@ class MainActions {
canShow: (viewModel) => viewModel.hasBuyAction,
onTap: (BuildContext context, DashboardViewModel viewModel) async {
if (!viewModel.isEnabledBuyAction) {
await _showErrorDialog(context, S.of(context).unsupported_asset);
await _showErrorDialog(
context, S.of(context).buy, S.of(context).unsupported_asset);
return;
}
final defaultBuyProvider = viewModel.defaultBuyProvider;
try {
await _launchProviderByType(context, defaultBuyProvider);
defaultBuyProvider != null
? await defaultBuyProvider.launchProvider(context, true)
: await Navigator.of(context).pushNamed(Routes.buySellPage, arguments: true);
} catch (e) {
await _showErrorDialog(context, e.toString());
await _showErrorDialog(context, defaultBuyProvider.toString(), e.toString());
}
},
);
static Future<void> _launchProviderByType(BuildContext context, BuyProviderType providerType) async {
switch (providerType) {
case BuyProviderType.AskEachTime:
Navigator.pushNamed(context, Routes.buy);
break;
case BuyProviderType.Onramper:
await getIt.get<OnRamperBuyProvider>().launchProvider(context);
break;
case BuyProviderType.Robinhood:
await getIt.get<RobinhoodBuyProvider>().launchProvider(context);
break;
case BuyProviderType.DFX:
await getIt.get<DFXBuyProvider>().launchProvider(context);
break;
default:
throw UnsupportedError('Unsupported buy provider type');
}
}
static Future<void> _showErrorDialog(BuildContext context, String errorMessage) async {
await showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: S.of(context).buy,
alertContent: errorMessage,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop(),
);
},
);
}
static MainActions receiveAction = MainActions._(
name: (context) => S.of(context).receive,
image: 'assets/images/received.png',
@@ -127,42 +87,35 @@ class MainActions {
isEnabled: (viewModel) => viewModel.isEnabledSellAction,
canShow: (viewModel) => viewModel.hasSellAction,
onTap: (BuildContext context, DashboardViewModel viewModel) async {
final walletType = viewModel.type;
if (!viewModel.isEnabledSellAction) {
await _showErrorDialog(
context, S.of(context).sell, S.of(context).unsupported_asset);
return;
}
switch (walletType) {
case WalletType.bitcoin:
case WalletType.litecoin:
case WalletType.ethereum:
case WalletType.polygon:
case WalletType.bitcoinCash:
if (viewModel.isEnabledSellAction) {
final moonPaySellProvider = MoonPaySellProvider();
final uri = await moonPaySellProvider.requestUrl(
currency: viewModel.wallet.currency,
refundWalletAddress: viewModel.wallet.walletAddresses.address,
settingsStore: viewModel.settingsStore,
);
if (DeviceInfo.instance.isMobile) {
Navigator.of(context).pushNamed(Routes.webViewPage,
arguments: [S.of(context).sell, uri]);
} else {
await launchUrl(uri);
}
}
break;
default:
await showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: S.of(context).sell,
alertContent: S.of(context).unsupported_asset,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop());
},
);
final defaultSellProvider = viewModel.defaultSellProvider;
try {
defaultSellProvider != null
? await defaultSellProvider.launchProvider(context, false)
: await Navigator.of(context).pushNamed(Routes.buySellPage, arguments: false);
} catch (e) {
await _showErrorDialog(context, defaultSellProvider.toString(), e.toString());
}
},
);
}
static Future<void> _showErrorDialog(
BuildContext context, String title, String errorMessage) async {
await showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: title,
alertContent: errorMessage,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop(),
);
},
);
}
}
+4 -2
View File
@@ -390,8 +390,10 @@ Route<dynamic> createRoute(RouteSettings settings) {
return MaterialPageRoute<void>(
builder: (_) => getIt.get<OrderDetailsPage>(param1: settings.arguments as Order));
case Routes.buy:
return MaterialPageRoute<void>(builder: (_) => getIt.get<BuyOptionsPage>());
case Routes.buySellPage:
final args = settings.arguments as bool;
return MaterialPageRoute<void>(
builder: (_) => getIt.get<BuySellOptionsPage>(param1: args));
case Routes.buyWebView:
final args = settings.arguments as List;
+1 -1
View File
@@ -55,7 +55,7 @@ class Routes {
static const supportLiveChat = '/support/live_chat';
static const supportOtherLinks = '/support/other';
static const orderDetails = '/order_details';
static const buy = '/buy';
static const buySellPage = '/buy_sell_page';
static const buyWebView = '/buy_web_view';
static const unspentCoinsList = '/unspent_coins_list';
static const unspentCoinsDetails = '/unspent_coins_details';
+39 -69
View File
@@ -1,7 +1,4 @@
import 'package:cake_wallet/buy/dfx/dfx_buy_provider.dart';
import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart';
import 'package:cake_wallet/buy/robinhood/robinhood_buy_provider.dart';
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/buy/buy_provider.dart';
import 'package:cake_wallet/entities/buy_provider_types.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
@@ -11,19 +8,14 @@ import 'package:cake_wallet/themes/extensions/transaction_trade_theme.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:flutter/material.dart';
class BuyOptionsPage extends BasePage {
BuyOptionsPage(this.dashboardViewModel);
class BuySellOptionsPage extends BasePage {
BuySellOptionsPage(this.dashboardViewModel, this.isBuyAction);
final DashboardViewModel dashboardViewModel;
final iconDarkRobinhood = 'assets/images/robinhood_dark.png';
final iconLightRobinhood = 'assets/images/robinhood_light.png';
final iconDarkOnramper = 'assets/images/onramper_dark.png';
final iconLightOnramper = 'assets/images/onramper_light.png';
final iconDarkDFX = 'assets/images/dfx_dark.png';
final iconLightDFX = 'assets/images/dfx_light.png';
final bool isBuyAction;
@override
String get title => S.current.buy;
String get title => isBuyAction ? S.current.buy : S.current.sell;
@override
AppBarStyle get appBarStyle => AppBarStyle.regular;
@@ -32,17 +24,9 @@ class BuyOptionsPage extends BasePage {
Widget body(BuildContext context) {
final isLightMode =
Theme.of(context).extension<OptionTileTheme>()?.useDarkImage ?? false;
final iconRobinhood = Image.asset(
isLightMode ? iconLightRobinhood : iconDarkRobinhood,
height: 40,
width: 40);
final iconOnramper = Image.asset(
isLightMode ? iconLightOnramper : iconDarkOnramper,
height: 40,
width: 40);
final iconDFX = Image.asset(isLightMode ? iconLightDFX : iconDarkDFX,
height: 40, width: 40);
final availableProviders = dashboardViewModel.availableProviders;
final availableProviders = isBuyAction
? dashboardViewModel.availableBuyProviders
: dashboardViewModel.availableSellProviders;
return Container(
child: Center(
@@ -50,57 +34,43 @@ class BuyOptionsPage extends BasePage {
constraints: BoxConstraints(maxWidth: 330),
child: Column(
children: [
if (availableProviders.contains(BuyProviderType.Onramper))
Padding(
...availableProviders.map((provider) {
final icon = Image.asset(
isLightMode ? provider.lightIcon : provider.darkIcon,
height: 40,
width: 40,
);
return Padding(
padding: EdgeInsets.only(top: 24),
child: OptionTile(
image: iconOnramper,
title: "Onramper",
description: S.of(context).onramper_option_description,
onPressed: () async => await getIt
.get<OnRamperBuyProvider>()
.launchProvider(context),
image: icon,
title: provider.toString(),
description: isBuyAction
? provider.buyOptionDescription
: provider.sellOptionDescription,
onPressed: () =>
provider.launchProvider(context, isBuyAction),
),
),
if (availableProviders.contains(BuyProviderType.Robinhood))
Padding(
padding: EdgeInsets.only(top: 24),
child: OptionTile(
image: iconRobinhood,
title: "Robinhood Connect",
description: S.of(context).robinhood_option_description,
onPressed: () async => await getIt
.get<RobinhoodBuyProvider>()
.launchProvider(context),
),
),
if (availableProviders.contains(BuyProviderType.DFX))
Padding(
padding: EdgeInsets.only(top: 24),
child: OptionTile(
image: iconDFX,
title: "DFX Connect",
description: S.of(context).dfx_option_description,
onPressed: () async => await getIt
.get<DFXBuyProvider>()
.launchProvider(context),
),
),
);
}).toList(),
Spacer(),
Padding(
padding: EdgeInsets.fromLTRB(24, 24, 24, 32),
child: Text(
S.of(context).select_buy_provider_notice,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.normal,
color: Theme.of(context)
.extension<TransactionTradeTheme>()!
.detailsTitlesColor,
Padding(
padding: EdgeInsets.fromLTRB(24, 24, 24, 32),
child: Text(
isBuyAction
? S.of(context).select_buy_provider_notice
: S.of(context).select_sell_provider_notice,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.normal,
color: Theme.of(context)
.extension<TransactionTradeTheme>()!
.detailsTitlesColor,
),
),
),
),
],
),
),
@@ -29,11 +29,10 @@ class BuyListItem extends StatelessWidget {
@override
Widget build(BuildContext context) {
final isSelected = selectedProvider?.description == provider.description;
final isSelected = selectedProvider?.buyOptionDescription == provider.buyOptionDescription;
final iconColor = isSelected ? Colors.white : Colors.black;
final providerIcon = getBuyProviderIcon(provider.description,
iconColor: iconColor)!;
final providerIcon = Image.asset('assets/images/wyre-icon.png', width: 36, height: 36);
final backgroundColor = isSelected
? Palette.greyBlueCraiola
@@ -76,7 +75,7 @@ class BuyListItem extends StatelessWidget {
padding: EdgeInsets.only(right: 10),
child: providerIcon),
Text(
provider.description.title,
provider.title,
style: TextStyle(
color: secondaryTextColor,
fontSize: 20,
+23 -1
View File
@@ -7,6 +7,7 @@ import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_sideba
import 'package:cake_wallet/src/screens/dashboard/pages/market_place_page.dart';
import 'package:cake_wallet/src/screens/wallet_connect/widgets/modals/bottom_sheet_listener.dart';
import 'package:cake_wallet/src/widgets/gradient_background.dart';
import 'package:cake_wallet/src/widgets/vulnerable_seeds_popup.dart';
import 'package:cake_wallet/themes/extensions/sync_indicator_theme.dart';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/utils/version_comparator.dart';
@@ -60,7 +61,8 @@ class DashboardPage extends StatelessWidget {
);
if (DeviceInfo.instance.isDesktop) {
if (responsiveLayoutUtil.screenWidth > ResponsiveLayoutUtilBase.kDesktopMaxDashBoardWidthConstraint) {
if (responsiveLayoutUtil.screenWidth >
ResponsiveLayoutUtilBase.kDesktopMaxDashBoardWidthConstraint) {
return getIt.get<DesktopSidebarWrapper>();
} else {
return dashboardPageView;
@@ -295,6 +297,8 @@ class _DashboardPageView extends BasePage {
_showReleaseNotesPopup(context);
_showVulnerableSeedsPopup(context);
var needToPresentYat = false;
var isInactive = false;
@@ -354,4 +358,22 @@ class _DashboardPageView extends BasePage {
sharedPrefs.setInt(PreferencesKey.lastSeenAppVersion, currentAppVersion);
}
}
void _showVulnerableSeedsPopup(BuildContext context) async {
final List<String> affectedWalletNames = await dashboardViewModel.checkAffectedWallets();
if (affectedWalletNames.isNotEmpty) {
Future<void>.delayed(
Duration(seconds: 1),
() {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return VulnerableSeedsPopup(affectedWalletNames);
},
);
},
);
}
}
}
@@ -3,6 +3,7 @@ import 'package:cake_wallet/entities/preferences_key.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/release_notes/release_notes_screen.dart';
import 'package:cake_wallet/src/screens/yat_emoji_id.dart';
import 'package:cake_wallet/src/widgets/vulnerable_seeds_popup.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/utils/version_comparator.dart';
import 'package:flutter/material.dart';
@@ -110,5 +111,25 @@ class DesktopDashboardPage extends StatelessWidget {
} else if (isNewInstall!) {
sharedPrefs.setInt(PreferencesKey.lastSeenAppVersion, currentAppVersion);
}
_showVulnerableSeedsPopup(context);
}
void _showVulnerableSeedsPopup(BuildContext context) async {
final List<String> affectedWalletNames = await dashboardViewModel.checkAffectedWallets();
if (affectedWalletNames.isNotEmpty) {
Future<void>.delayed(
Duration(seconds: 1),
() {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return VulnerableSeedsPopup(affectedWalletNames);
},
);
},
);
}
}
}
@@ -54,6 +54,24 @@ class BalancePage extends StatelessWidget {
Theme.of(context).extension<DashboardPageTheme>()!.pageTitleTextColor,
height: 1,
),
unselectedLabelStyle: TextStyle(
fontSize: 18,
fontFamily: 'Lato',
fontWeight: FontWeight.w600,
color:
Theme.of(context).extension<DashboardPageTheme>()!.pageTitleTextColor,
height: 1,
),
labelColor:
Theme.of(context).extension<DashboardPageTheme>()!.pageTitleTextColor,
dividerColor:
Theme.of(context).extension<DashboardPageTheme>()!.pageTitleTextColor,
indicatorColor:
Theme.of(context).extension<DashboardPageTheme>()!.pageTitleTextColor,
unselectedLabelColor: Theme.of(context)
.extension<DashboardPageTheme>()!
.pageTitleTextColor
.withOpacity(0.5),
tabs: [
Tab(text: 'My Crypto'),
Tab(text: 'My NFTs'),
@@ -411,10 +411,6 @@ class ExchangePage extends BasePage {
}
});
reaction((_) => exchangeViewModel.isReceiveAddressEnabled, (bool isEnabled) {
receiveKey.currentState!.isAddressEditable(isEditable: isEnabled);
});
reaction((_) => exchangeViewModel.isReceiveAmountEditable, (bool isReceiveAmountEditable) {
receiveKey.currentState!.isAmountEditable(isEditable: isReceiveAmountEditable);
});
@@ -670,7 +666,6 @@ class ExchangePage extends BasePage {
? exchangeViewModel.wallet.walletAddresses.address
: exchangeViewModel.receiveAddress,
initialIsAmountEditable: exchangeViewModel.isReceiveAmountEditable,
initialIsAddressEditable: exchangeViewModel.isReceiveAddressEnabled,
isAmountEstimated: true,
isMoneroWallet: exchangeViewModel.isMoneroWallet,
currencies: exchangeViewModel.receiveCurrencies,
@@ -174,8 +174,6 @@ class ExchangeTemplatePage extends BasePage {
? exchangeViewModel.wallet.walletAddresses.address
: exchangeViewModel.receiveAddress,
initialIsAmountEditable: false,
initialIsAddressEditable:
exchangeViewModel.isReceiveAddressEnabled,
isAmountEstimated: true,
isMoneroWallet: exchangeViewModel.isMoneroWallet,
currencies: exchangeViewModel.receiveCurrencies,
@@ -328,11 +326,6 @@ class ExchangeTemplatePage extends BasePage {
}
});
reaction((_) => exchangeViewModel.isReceiveAddressEnabled,
(bool isEnabled) {
receiveKey.currentState!.isAddressEditable(isEditable: isEnabled);
});
reaction((_) => exchangeViewModel.provider, (ExchangeProvider? provider) {
receiveKey.currentState!.isAmountEditable(isEditable: false);
});
@@ -23,7 +23,6 @@ class ExchangeCard extends StatefulWidget {
required this.initialAddress,
required this.initialWalletName,
required this.initialIsAmountEditable,
required this.initialIsAddressEditable,
required this.isAmountEstimated,
required this.currencies,
required this.onCurrencySelected,
@@ -31,6 +30,7 @@ class ExchangeCard extends StatefulWidget {
this.currencyValueValidator,
this.addressTextFieldValidator,
this.title = '',
this.initialIsAddressEditable = true,
this.hasRefundAddress = false,
this.isMoneroWallet = false,
this.currencyButtonColor = Colors.transparent,
@@ -43,10 +43,18 @@ class OtherSettingsPage extends BasePage {
if(_otherSettingsViewModel.isEnabledBuyAction)
SettingsPickerCell(
title: S.current.default_buy_provider,
items: _otherSettingsViewModel.availableBuyProviders,
items: _otherSettingsViewModel.availableBuyProvidersTypes,
displayItem: _otherSettingsViewModel.getBuyProviderType,
selectedItem: _otherSettingsViewModel.buyProviderType,
onItemSelected: _otherSettingsViewModel.onBuyProviderTypeSelected,
onItemSelected: _otherSettingsViewModel.onBuyProviderTypeSelected
),
if(_otherSettingsViewModel.isEnabledSellAction)
SettingsPickerCell(
title: S.current.default_sell_provider,
items: _otherSettingsViewModel.availableSellProvidersTypes,
displayItem: _otherSettingsViewModel.getSellProviderType,
selectedItem: _otherSettingsViewModel.sellProviderType,
onItemSelected: _otherSettingsViewModel.onSellProviderTypeSelected,
),
SettingsCellWithArrow(
title: S.current.settings_terms_and_conditions,
@@ -0,0 +1,91 @@
import 'package:cake_wallet/src/widgets/alert_background.dart';
import 'package:cake_wallet/src/widgets/alert_close_button.dart';
import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart';
import 'package:flutter/material.dart';
class VulnerableSeedsPopup extends StatelessWidget {
final List<String> affectedWalletNames;
const VulnerableSeedsPopup(this.affectedWalletNames, {Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Stack(
alignment: Alignment.center,
children: [
AlertBackground(
child: AlertDialog(
insetPadding: EdgeInsets.only(left: 16, right: 16, bottom: 48),
elevation: 0.0,
contentPadding: EdgeInsets.zero,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(30))),
content: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30.0),
gradient: LinearGradient(colors: [
Theme.of(context).extension<DashboardPageTheme>()!.firstGradientBackgroundColor,
Theme.of(context)
.extension<DashboardPageTheme>()!
.secondGradientBackgroundColor,
], begin: Alignment.centerLeft, end: Alignment.centerRight)),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.0),
child: Stack(
children: [
SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.only(top: 16.0),
child: Container(
alignment: Alignment.bottomCenter,
child: DefaultTextStyle(
style: TextStyle(
decoration: TextDecoration.none,
fontSize: 24.0,
fontWeight: FontWeight.bold,
fontFamily: 'Lato',
color: Theme.of(context).extension<DashboardPageTheme>()!.textColor,
),
child: Text("Emergency Notice"),
),
),
),
),
SingleChildScrollView(
child: Padding(
padding: EdgeInsets.only(top: 48, bottom: 16),
child: Container(
width: double.maxFinite,
child: Column(
children: <Widget>[
ConstrainedBox(
constraints: BoxConstraints(
maxHeight: MediaQuery.of(context).size.height * 0.7,
),
child: Text(
"Your Bitcoin wallet(s) below use a legacy seed format that is vulnerable, which MAY result in you losing money from these wallet(s) if no action is taken.\nWe recommend that you IMMEDIATELY create wallet(s) in Cake Wallet and immediately transfer the funds to these wallet(s).\nVulnerable wallet name(s):\n\n[${affectedWalletNames.join(", ")}]\n\nFor assistance, please use the in-app support or email support@cakewallet.com",
style: TextStyle(
decoration: TextDecoration.none,
fontSize: 16.0,
fontFamily: 'Lato',
color: Theme.of(context)
.extension<DashboardPageTheme>()!
.textColor,
),
),
)
],
),
),
),
),
],
),
),
),
),
),
AlertCloseButton(bottom: 30)
],
);
}
}
+6 -7
View File
@@ -26,8 +26,7 @@ abstract class AppStoreBase with Store {
AuthenticationStore authenticationStore;
@observable
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo>?
wallet;
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo>? wallet;
WalletListStore walletList;
@@ -36,16 +35,16 @@ abstract class AppStoreBase with Store {
NodeListStore nodeListStore;
@action
void changeCurrentWallet(
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>,
TransactionInfo>
wallet) {
Future<void> changeCurrentWallet(
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo> wallet) async {
this.wallet?.close();
this.wallet = wallet;
this.wallet!.setExceptionHandler(ExceptionHandler.onError);
if (isEVMCompatibleChain(wallet.type)) {
getIt.get<Web3WalletService>().init();
await getIt.get<Web3WalletService>().onDispose();
getIt.get<Web3WalletService>().create();
await getIt.get<Web3WalletService>().init();
}
}
}
+41 -15
View File
@@ -2,6 +2,7 @@ import 'dart:io';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
import 'package:cake_wallet/buy/buy_provider.dart';
import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
import 'package:cake_wallet/entities/buy_provider_types.dart';
import 'package:cake_wallet/entities/cake_2fa_preset_options.dart';
@@ -147,7 +148,9 @@ abstract class SettingsStoreBase with Store {
initialShouldRequireTOTP2FAForAllSecurityAndBackupSettings,
currentSyncMode = initialSyncMode,
currentSyncAll = initialSyncAll,
priority = ObservableMap<WalletType, TransactionPriority>() {
priority = ObservableMap<WalletType, TransactionPriority>(),
defaultBuyProviders = ObservableMap<WalletType, BuyProviderType>(),
defaultSellProviders = ObservableMap<WalletType, BuyProviderType>() {
//this.nodes = ObservableMap<WalletType, Node>.of(nodes);
if (initialMoneroTransactionPriority != null) {
@@ -181,9 +184,25 @@ abstract class SettingsStoreBase with Store {
initializeTrocadorProviderStates();
WalletType.values.forEach((walletType) {
final key = 'defaultBuyProvider_${walletType.toString()}';
final providerIndex = sharedPreferences.getInt(key);
defaultBuyProviders[walletType] = providerIndex != null ? BuyProviderType.values[providerIndex] : BuyProviderType.AskEachTime;
final key = 'buyProvider_${walletType.toString()}';
final providerId = sharedPreferences.getString(key);
if (providerId != null) {
defaultBuyProviders[walletType] = BuyProviderType.values
.firstWhere((provider) => provider.id == providerId, orElse: () => BuyProviderType.askEachTime);
} else {
defaultBuyProviders[walletType] = BuyProviderType.askEachTime;
}
});
WalletType.values.forEach((walletType) {
final key = 'sellProvider_${walletType.toString()}';
final providerId = sharedPreferences.getString(key);
if (providerId != null) {
defaultSellProviders[walletType] = BuyProviderType.values
.firstWhere((provider) => provider.id == providerId, orElse: () => BuyProviderType.askEachTime);
} else {
defaultSellProviders[walletType] = BuyProviderType.askEachTime;
}
});
reaction(
@@ -196,6 +215,20 @@ abstract class SettingsStoreBase with Store {
(bool shouldShowYatPopup) =>
sharedPreferences.setBool(PreferencesKey.shouldShowYatPopup, shouldShowYatPopup));
defaultBuyProviders.observe((change) {
final String key = 'buyProvider_${change.key.toString()}';
if (change.newValue != null) {
sharedPreferences.setString(key, change.newValue!.id);
}
});
defaultSellProviders.observe((change) {
final String key = 'sellProvider_${change.key.toString()}';
if (change.newValue != null) {
sharedPreferences.setString(key, change.newValue!.id);
}
});
priority.observe((change) {
final String? key;
switch (change.key) {
@@ -251,16 +284,6 @@ abstract class SettingsStoreBase with Store {
(bool disableSell) =>
sharedPreferences.setBool(PreferencesKey.disableSellKey, disableSell));
reaction(
(_) => defaultBuyProviders.asObservable(),
(ObservableMap<WalletType, BuyProviderType> providers) {
providers.forEach((walletType, provider) {
final key = 'defaultBuyProvider_${walletType.toString()}';
sharedPreferences.setInt(key, provider.index);
});
}
);
reaction(
(_) => walletListOrder,
(WalletListOrderType walletListOrder) =>
@@ -594,7 +617,10 @@ abstract class SettingsStoreBase with Store {
ObservableMap<String, bool> trocadorProviderStates = ObservableMap<String, bool>();
@observable
ObservableMap<WalletType, BuyProviderType> defaultBuyProviders = ObservableMap<WalletType, BuyProviderType>();
ObservableMap<WalletType, BuyProviderType> defaultBuyProviders;
@observable
ObservableMap<WalletType, BuyProviderType> defaultSellProviders;
@observable
SortBalanceBy sortBalanceBy;
+1 -2
View File
@@ -61,8 +61,7 @@ abstract class BuyViewModelBase with Store {
String _url = '';
try {
_url = await selectedProvider
!.requestUrl(doubleAmount.toString(), fiatCurrency.title);
_url = await selectedProvider!.requestUrl(doubleAmount.toString(), fiatCurrency.title);
} catch (e) {
print(e.toString());
}
@@ -1,3 +1,7 @@
import 'dart:convert';
import 'package:cake_wallet/buy/buy_provider.dart';
import 'package:cake_wallet/core/key_service.dart';
import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
import 'package:cake_wallet/entities/balance_display_mode.dart';
import 'package:cake_wallet/entities/buy_provider_types.dart';
@@ -24,13 +28,21 @@ import 'package:cake_wallet/view_model/dashboard/trade_list_item.dart';
import 'package:cake_wallet/view_model/dashboard/transaction_list_item.dart';
import 'package:cake_wallet/view_model/settings/sync_mode.dart';
import 'package:cake_wallet/wallet_type_utils.dart';
import 'package:cryptography/cryptography.dart';
import 'package:cw_core/balance.dart';
import 'package:cw_core/cake_hive.dart';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/sync_status.dart';
import 'package:cw_core/transaction_history.dart';
import 'package:cw_core/transaction_info.dart';
import 'package:cw_core/utils/file.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:eth_sig_util/util/utils.dart';
import 'package:flutter/services.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/entities/buy_provider_types.dart';
part 'dashboard_view_model.g.dart';
@@ -46,7 +58,8 @@ abstract class DashboardViewModelBase with Store {
required this.settingsStore,
required this.yatStore,
required this.ordersStore,
required this.anonpayTransactionsStore})
required this.anonpayTransactionsStore,
required this.keyService})
: hasSellAction = false,
hasBuyAction = false,
hasExchangeAction = false,
@@ -262,6 +275,8 @@ abstract class DashboardViewModelBase with Store {
bool get hasRescan => wallet.type == WalletType.monero || wallet.type == WalletType.haven;
final KeyService keyService;
BalanceViewModel balanceViewModel;
AppStore appStore;
@@ -282,13 +297,31 @@ abstract class DashboardViewModelBase with Store {
Map<String, List<FilterItem>> filterItems;
BuyProviderType get defaultBuyProvider =>
settingsStore.defaultBuyProviders[wallet.type] ??
BuyProviderType.AskEachTime;
BuyProvider? get defaultBuyProvider => BuyProviderHelper.getProviderByType(
settingsStore.defaultBuyProviders[wallet.type] ?? BuyProviderType.askEachTime);
BuyProvider? get defaultSellProvider => BuyProviderHelper.getProviderByType(
settingsStore.defaultSellProviders[wallet.type] ?? BuyProviderType.askEachTime);
bool get isBuyEnabled => settingsStore.isBitcoinBuyEnabled;
List<BuyProviderType> get availableProviders => BuyProviderType.getAvailableProviders(wallet.type);
List<BuyProvider> get availableBuyProviders {
final providerTypes = BuyProviderHelper.getAvailableBuyProviderTypes(wallet.type);
return providerTypes
.map((type) => BuyProviderHelper.getProviderByType(type))
.where((provider) => provider != null)
.cast<BuyProvider>()
.toList();
}
List<BuyProvider> get availableSellProviders {
final providerTypes = BuyProviderHelper.getAvailableSellProviderTypes(wallet.type);
return providerTypes
.map((type) => BuyProviderHelper.getProviderByType(type))
.where((provider) => provider != null)
.cast<BuyProvider>()
.toList();
}
bool get shouldShowYatPopup => settingsStore.shouldShowYatPopup;
@@ -302,16 +335,15 @@ abstract class DashboardViewModelBase with Store {
bool hasExchangeAction;
@computed
bool get isEnabledBuyAction => !settingsStore.disableBuy && wallet.type != WalletType.haven;
bool get isEnabledBuyAction =>
!settingsStore.disableBuy && availableBuyProviders.isNotEmpty;
@observable
bool hasBuyAction;
@computed
bool get isEnabledSellAction =>
!settingsStore.disableSell &&
wallet.type != WalletType.haven &&
wallet.type != WalletType.monero;
!settingsStore.disableSell && availableSellProviders.isNotEmpty;
@observable
bool hasSellAction;
@@ -433,4 +465,32 @@ abstract class DashboardViewModelBase with Store {
@action
void setSyncAll(bool value) => settingsStore.currentSyncAll = value;
Future<List<String>> checkAffectedWallets() async {
// await load file
final vulnerableSeedsString = await rootBundle.loadString('assets/text/cakewallet_weak_bitcoin_seeds_hashed_sorted_version1.txt');
final vulnerableSeeds = vulnerableSeedsString.split("\n");
final walletInfoSource = await CakeHive.openBox<WalletInfo>(WalletInfo.boxName);
List<String> affectedWallets = [];
for (var walletInfo in walletInfoSource.values) {
if (walletInfo.type == WalletType.bitcoin) {
final password = await keyService.getWalletPassword(walletName: walletInfo.name);
final path = await pathForWallet(name: walletInfo.name, type: walletInfo.type);
final jsonSource = await read(path: path, password: password);
final data = json.decode(jsonSource) as Map;
final mnemonic = data['mnemonic'] as String;
final hash = await Cryptography.instance.sha256().hash(utf8.encode(mnemonic));
final seedSha = bytesToHex(hash.bytes);
if (vulnerableSeeds.contains(seedSha)) {
affectedWallets.add(walletInfo.name);
}
}
}
return affectedWallets;
}
}
@@ -66,7 +66,6 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
receiveAddress = '',
depositAddress = '',
isDepositAddressEnabled = false,
isReceiveAddressEnabled = false,
isReceiveAmountEditable = false,
_useTorOnly = false,
receiveCurrencies = <CryptoCurrency>[],
@@ -108,7 +107,6 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
bestRateSync = Timer.periodic(Duration(seconds: 10), (timer) => _calculateBestRate());
isDepositAddressEnabled = !(depositCurrency == wallet.currency);
isReceiveAddressEnabled = !(receiveCurrency == wallet.currency);
depositAmount = '';
receiveAmount = '';
receiveAddress = '';
@@ -201,9 +199,6 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
@observable
bool isDepositAddressEnabled;
@observable
bool isReceiveAddressEnabled;
@observable
bool isReceiveAmountEntered;
@@ -315,7 +310,6 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
isFixedRateMode = false;
_onPairChange();
isDepositAddressEnabled = !(depositCurrency == wallet.currency);
isReceiveAddressEnabled = !(receiveCurrency == wallet.currency);
}
@action
@@ -324,7 +318,6 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
isFixedRateMode = false;
_onPairChange();
isDepositAddressEnabled = !(depositCurrency == wallet.currency);
isReceiveAddressEnabled = !(receiveCurrency == wallet.currency);
}
@action
@@ -535,7 +528,6 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
depositAddress = depositCurrency == wallet.currency ? wallet.walletAddresses.address : '';
receiveAddress = receiveCurrency == wallet.currency ? wallet.walletAddresses.address : '';
isDepositAddressEnabled = !(depositCurrency == wallet.currency);
isReceiveAddressEnabled = !(receiveCurrency == wallet.currency);
isFixedRateMode = false;
_onPairChange();
}
+24 -15
View File
@@ -50,8 +50,10 @@ abstract class OrderDetailsViewModelBase with Store {
@action
Future<void> _updateOrder() async {
try {
if (_provider != null) {
final updatedOrder = await _provider!.findOrderById(order.id);
if (_provider != null && (_provider is MoonPayBuyProvider || _provider is WyreBuyProvider)) {
final updatedOrder = _provider is MoonPayBuyProvider
? await (_provider as MoonPayBuyProvider).findOrderById(order.id)
: await (_provider as WyreBuyProvider).findOrderById(order.id);
updatedOrder.from = order.from;
updatedOrder.to = order.to;
updatedOrder.receiveAddress = order.receiveAddress;
@@ -87,19 +89,26 @@ abstract class OrderDetailsViewModelBase with Store {
value: order.provider.title)
);
if (_provider?.trackUrl.isNotEmpty ?? false) {
final buildURL = _provider!.trackUrl + '${order.transferId}';
items.add(
TrackTradeListItem(
title: 'Track',
value: buildURL,
onTap: () {
try {
launch(buildURL);
} catch (e) {}
}
)
);
if (_provider != null && (_provider is MoonPayBuyProvider || _provider is WyreBuyProvider)) {
final trackUrl = _provider is MoonPayBuyProvider
? (_provider as MoonPayBuyProvider).trackUrl
: (_provider as WyreBuyProvider).trackUrl;
if (trackUrl.isNotEmpty ?? false) {
final buildURL = trackUrl + '${order.transferId}';
items.add(
TrackTradeListItem(
title: 'Track',
value: buildURL,
onTap: () {
try {
launch(buildURL);
} catch (e) {}
}
)
);
}
}
items.add(
@@ -1,6 +1,7 @@
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/entities/buy_provider_types.dart';
import 'package:cake_wallet/entities/priority_for_wallet_type.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cw_core/balance.dart';
import 'package:cw_core/transaction_history.dart';
@@ -59,12 +60,24 @@ abstract class OtherSettingsViewModelBase with Store {
bool get isEnabledBuyAction =>
!_settingsStore.disableBuy && _wallet.type != WalletType.haven;
List<BuyProviderType> get availableBuyProviders =>
BuyProviderType.getAvailableProviders(walletType);
@computed
bool get isEnabledSellAction =>
!_settingsStore.disableSell && _wallet.type != WalletType.haven;
List<BuyProviderType> get availableBuyProvidersTypes {
return BuyProviderHelper.getAvailableBuyProviderTypes(walletType);
}
List<BuyProviderType> get availableSellProvidersTypes =>
BuyProviderHelper.getAvailableSellProviderTypes(walletType);
BuyProviderType get buyProviderType =>
_settingsStore.defaultBuyProviders[walletType] ??
BuyProviderType.AskEachTime;
BuyProviderType.askEachTime;
BuyProviderType get sellProviderType =>
_settingsStore.defaultSellProviders[walletType] ??
BuyProviderType.askEachTime;
String getDisplayPriority(dynamic priority) {
final _priority = priority as TransactionPriority;
@@ -81,13 +94,27 @@ abstract class OtherSettingsViewModelBase with Store {
String getBuyProviderType(dynamic buyProviderType) {
final _buyProviderType = buyProviderType as BuyProviderType;
return _buyProviderType == BuyProviderType.askEachTime
? S.current.ask_each_time
: _buyProviderType.name;
}
return _buyProviderType.toString();
String getSellProviderType(dynamic sellProviderType) {
final _sellProviderType = sellProviderType as BuyProviderType;
return _sellProviderType == BuyProviderType.askEachTime
? S.current.ask_each_time
: _sellProviderType.name;
}
void onDisplayPrioritySelected(TransactionPriority priority) =>
_settingsStore.priority[_wallet.type] = priority;
void onBuyProviderTypeSelected(BuyProviderType buyProviderType) =>
@action
BuyProviderType onBuyProviderTypeSelected(BuyProviderType buyProviderType) =>
_settingsStore.defaultBuyProviders[walletType] = buyProviderType;
@action
BuyProviderType onSellProviderTypeSelected(
BuyProviderType sellProviderType) =>
_settingsStore.defaultSellProviders[walletType] = sellProviderType;
}
+1 -1
View File
@@ -74,7 +74,7 @@ abstract class WalletCreationVMBase with Store {
: await process(credentials);
walletInfo.address = wallet.walletAddresses.address;
await _walletInfoSource.add(walletInfo);
_appStore.changeCurrentWallet(wallet);
await _appStore.changeCurrentWallet(wallet);
getIt.get<BackgroundTasks>().registerSyncTask();
_appStore.authenticationStore.allowed();
state = ExecutedSuccessfullyState();
@@ -45,7 +45,7 @@ abstract class WalletListViewModelBase with Store {
@action
Future<void> loadWallet(WalletListItem walletItem) async {
final wallet = await _walletLoadingService.load(walletItem.type, walletItem.name);
_appStore.changeCurrentWallet(wallet);
await _appStore.changeCurrentWallet(wallet);
}
WalletListOrderType? get orderType => _appStore.settingsStore.walletListOrder;