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

This commit is contained in:
Serhii 2023-12-30 16:58:46 +02:00
commit a5895e8832
76 changed files with 9627 additions and 492 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View file

@ -1,2 +1,4 @@
Monero Polyseed support, create and restore from a 16 words phrase and without the need to remember the wallet creation date
Bug fixes and enhancements
Polyseed enhancements
New on-ramp provider DFX
Usability enhancements
Bug fixes

View file

@ -1,3 +1,2 @@
Monero Polyseed support, create and restore from a 16 words phrase and without the need to remember the wallet creation date
Add NFTs tab to see all of your purchased NFTs on Ethereum
Bug fixes and enhancements
Support multiple address types for Bitcoin Cash
Bug fixes

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,7 @@
import 'dart:convert';
import 'package:bitbox/bitbox.dart' as bitbox;
import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
import 'package:cw_bitcoin/bitcoin_address_record.dart';
import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart';
@ -210,9 +211,28 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
txb.addInput(input.hash, input.vout);
});
final String bchPrefix = "bitcoincash:";
outputs.forEach((item) {
final outputAmount = hasMultiDestination ? item.formattedCryptoAmount : amount;
final outputAddress = item.isParsedAddress ? item.extractedAddress! : item.address;
String outputAddress = item.isParsedAddress ? item.extractedAddress! : item.address;
if (!outputAddress.startsWith(bchPrefix)) {
outputAddress = "$bchPrefix$outputAddress";
}
bool isP2sh = outputAddress.startsWith("p", bchPrefix.length);
if (isP2sh) {
final p2sh = P2shAddress.fromAddress(
address: outputAddress,
network: BitcoinCashNetwork.mainnet,
);
txb.addOutput(Uint8List.fromList(p2sh.toScriptPubKey().toBytes()), outputAmount!);
return;
}
txb.addOutput(outputAddress, outputAmount!);
});

View file

@ -29,6 +29,7 @@ dependencies:
git:
url: https://github.com/cake-tech/bitbox-flutter.git
ref: master
bitcoin_base: ^3.0.1

View file

@ -245,7 +245,7 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
}
static CryptoCurrency fromFullName(String name) {
if (CryptoCurrency._fullNameCurrencyMap[name.toLowerCase()] == null) {
if (CryptoCurrency._fullNameCurrencyMap[name.split("(").first.trim().toLowerCase()] == null) {
final s = 'Unexpected token: $name for CryptoCurrency fromFullName';
throw ArgumentError.value(name, 'Fullname', s);
}

View file

@ -623,15 +623,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.1"
tor:
dependency: transitive
description:
path: "."
ref: main
resolved-ref: "09ba92cb11d4e3cacf97256e57863b805f79f2e5"
url: "https://github.com/cake-tech/tor.git"
source: git
version: "0.0.1"
typed_data:
dependency: transitive
description:

View file

@ -122,6 +122,8 @@ PODS:
- Flutter
- MTBBarcodeScanner (5.0.11)
- OrderedSet (5.0.0)
- package_info (0.0.1):
- Flutter
- package_info_plus (0.4.5):
- Flutter
- path_provider_foundation (0.0.1):
@ -143,8 +145,6 @@ PODS:
- SwiftProtobuf (1.22.0)
- SwiftyGif (5.4.4)
- Toast (4.0.0)
- tor (0.0.1):
- Flutter
- uni_links (0.0.1):
- Flutter
- UnstoppableDomainsResolution (4.0.0):
@ -175,13 +175,13 @@ DEPENDENCIES:
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
- in_app_review (from `.symlinks/plugins/in_app_review/ios`)
- local_auth_ios (from `.symlinks/plugins/local_auth_ios/ios`)
- package_info (from `.symlinks/plugins/package_info/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
- sensitive_clipboard (from `.symlinks/plugins/sensitive_clipboard/ios`)
- share_plus (from `.symlinks/plugins/share_plus/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- tor (from `.symlinks/plugins/tor/ios`)
- uni_links (from `.symlinks/plugins/uni_links/ios`)
- UnstoppableDomainsResolution (~> 4.0.0)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
@ -236,6 +236,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/in_app_review/ios"
local_auth_ios:
:path: ".symlinks/plugins/local_auth_ios/ios"
package_info:
:path: ".symlinks/plugins/package_info/ios"
package_info_plus:
:path: ".symlinks/plugins/package_info_plus/ios"
path_provider_foundation:
@ -248,8 +250,6 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/share_plus/ios"
shared_preferences_foundation:
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
tor:
:path: ".symlinks/plugins/tor/ios"
uni_links:
:path: ".symlinks/plugins/uni_links/ios"
url_launcher_ios:
@ -282,6 +282,7 @@ SPEC CHECKSUMS:
local_auth_ios: c6cf091ded637a88f24f86a8875d8b0f526e2605
MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb
OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c
package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62
package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6
@ -293,7 +294,6 @@ SPEC CHECKSUMS:
SwiftProtobuf: 40bd808372cb8706108f22d28f8ab4a6b9bc6989
SwiftyGif: 93a1cc87bf3a51916001cf8f3d63835fb64c819f
Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
tor: 662a9f5b980b5c86decb8ba611de9bcd4c8286eb
uni_links: d97da20c7701486ba192624d99bffaaffcfc298a
UnstoppableDomainsResolution: c3c67f4d0a5e2437cb00d4bd50c2e00d6e743841
url_launcher_ios: bf5ce03e0e2088bad9cc378ea97fa0ed5b49673b

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';
}

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();
}

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);
}

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();
}
}

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);
}

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 {
});
}
}
}

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();
}

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:

View file

@ -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,

View file

@ -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();
}
}

View file

@ -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(

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;

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;
}
}
}

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();
}

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(),
);
},
);
}
}

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;

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';

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,
),
),
),
),
],
),
),

View file

@ -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,

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);
},
);
},
);
}
}
}

View file

@ -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);
},
);
},
);
}
}
}

View file

@ -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'),

View file

@ -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,

View file

@ -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);
});

View file

@ -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,

View file

@ -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,

View file

@ -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)
],
);
}
}

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();
}
}
}

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;

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());
}

View file

@ -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;
}
}

View file

@ -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();
}

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(

View file

@ -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;
}

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();

View file

@ -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;

View file

@ -41,7 +41,6 @@ PODS:
- shared_preferences_foundation (0.0.1):
- Flutter
- FlutterMacOS
- tor (0.0.1)
- url_launcher_macos (0.0.1):
- FlutterMacOS
- wakelock_plus (0.0.1):
@ -60,7 +59,6 @@ DEPENDENCIES:
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
- share_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/share_plus_macos/macos`)
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`)
- tor (from `Flutter/ephemeral/.symlinks/plugins/tor/macos`)
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
- wakelock_plus (from `Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos`)
@ -93,8 +91,6 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/share_plus_macos/macos
shared_preferences_foundation:
:path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin
tor:
:path: Flutter/ephemeral/.symlinks/plugins/tor/macos
url_launcher_macos:
:path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
wakelock_plus:
@ -102,7 +98,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
connectivity_plus_macos: f6e86fd000e971d361e54b5afcadc8c8fa773308
cw_monero: f8b7f104508efba2591548e76b5c058d05cba3f0
cw_monero: ec03de55a19c4a2b174ea687e0f4202edc716fa4
device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f
devicelocale: 9f0f36ac651cabae2c33f32dcff4f32b61c38225
flutter_secure_storage_macos: d56e2d218c1130b262bef8b4a7d64f88d7f9c9ea
@ -114,7 +110,6 @@ SPEC CHECKSUMS:
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
share_plus_macos: 853ee48e7dce06b633998ca0735d482dd671ade4
shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126
tor: 2138c48428e696b83eacdda404de6d5574932e26
url_launcher_macos: d2691c7dd33ed713bf3544850a623080ec693d95
wakelock_plus: 4783562c9a43d209c458cb9b30692134af456269

View file

@ -756,6 +756,8 @@
"dfx_option_description": "ﺎﺑﻭﺭﻭﺃ ﻲﻓ ﺕﺎﻛﺮﺸﻟﺍﻭ ﺔﺋﺰﺠﺘﻟﺍ ءﻼﻤﻌﻟ .ﻲﻓﺎﺿﺇ KYC ﻥﻭﺪﺑ ﻭﺭﻮﻳ 990 ﻰﻟﺇ ﻞﺼﻳ ﺎﻣ .ﻱﺮﺴﻳﻮﺴﻟﺍ",
"polygonscan_history": "ﻥﺎﻜﺴﻧﻮﺠﻴﻟﻮﺑ ﺦﻳﺭﺎﺗ",
"wallet_seed_legacy": "بذرة محفظة قديمة",
"default_sell_provider": " ﻲﺿﺍﺮﺘﻓﻻﺍ ﻊﻴﺒﻟﺍ ﺩﻭﺰﻣ",
"select_sell_provider_notice": ".ﻖﻴﺒﻄﺘﻟﺍ ﺕﺍﺩﺍﺪﻋﺇ ﻲﻓ ﻚﺑ ﺹﺎﺨﻟﺍ ﻲﺿﺍﺮﺘﻓﻻﺍ ﻊﻴﺒﻟﺍ ﺩﻭﺰﻣ ﻦﻴﻴﻌﺗ ﻖﻳﺮﻃ ﻦﻋ ﺔﺷﺎﺸﻟﺍ ﻩﺬﻫ ﻲﻄﺨﺗ",
"custom_drag": "مخصص (عقد وسحب)",
"switchToEVMCompatibleWallet": " (Ethereum، Polygon) ﻯﺮﺧﺃ ﺓﺮﻣ ﺔﻟﻭﺎﺤﻤﻟﺍﻭ EVM ﻊﻣ ﺔﻘﻓﺍﻮﺘﻣ ﺔﻈﻔﺤﻣ ﻰﻟﺇ ﻞﻳﺪﺒﺘﻟﺍ ﻰﺟﺮﻳ"
}

View file

@ -752,6 +752,8 @@
"dfx_option_description": "Купете крипто с EUR и CHF. До 990 € без допълнителен KYC. За клиенти на дребно и корпоративни клиенти в Европа",
"polygonscan_history": "История на PolygonScan",
"wallet_seed_legacy": "Наследено портфейл семе",
"default_sell_provider": "Доставчик за продажба по подразбиране",
"select_sell_provider_notice": "Изберете доставчик на продажба по-горе. Можете да пропуснете този екран, като зададете своя доставчик на продажба по подразбиране в настройките на приложението.",
"custom_drag": "Персонализиране (задръжте и плъзнете)",
"switchToEVMCompatibleWallet": "Моля, превключете към портфейл, съвместим с EVM, и опитайте отново (Ethereum, Polygon)"
}

View file

@ -752,6 +752,8 @@
"dfx_option_description": "Nakupujte kryptoměny za EUR a CHF. Až 990 € bez dalších KYC. Pro maloobchodní a firemní zákazníky v Evropě",
"polygonscan_history": "Historie PolygonScan",
"wallet_seed_legacy": "Starší semeno peněženky",
"default_sell_provider": "Výchozí poskytovatel prodeje",
"select_sell_provider_notice": "Výše vyberte poskytovatele prodeje. Tuto obrazovku můžete přeskočit nastavením výchozího poskytovatele prodeje v nastavení aplikace.",
"custom_drag": "Custom (Hold and Drag)",
"switchToEVMCompatibleWallet": "Přepněte na peněženku kompatibilní s EVM a zkuste to znovu (Ethereum, Polygon)"
}

View file

@ -760,6 +760,8 @@
"dfx_option_description": "Krypto mit EUR und CHF kaufen. Bis zu 990€ ohne zusätzliches KYC. Für Privat- und Firmenkunden in Europa",
"polygonscan_history": "PolygonScan-Verlauf",
"wallet_seed_legacy": "Legacy Wallet Seed",
"default_sell_provider": "Standard-Verkaufsanbieter",
"select_sell_provider_notice": "Wählen Sie oben einen Verkaufsanbieter aus. Sie können diesen Bildschirm überspringen, indem Sie in den App-Einstellungen Ihren Standard-Verkaufsanbieter festlegen.",
"custom_drag": "Custom (Hold and Drag)",
"switchToEVMCompatibleWallet": "Bitte wechseln Sie zu einem EVM-kompatiblen Wallet und versuchen Sie es erneut (Ethereum, Polygon)"
}

View file

@ -761,6 +761,8 @@
"dfx_option_description": "Buy crypto with EUR & CHF. Up to 990€ without additional KYC. For retail and corporate customers in Europe",
"polygonscan_history": "PolygonScan history",
"wallet_seed_legacy": "Legacy wallet seed",
"default_sell_provider": "Default Sell Provider",
"select_sell_provider_notice": "Select a sell provider above. You can skip this screen by setting your default sell provider in app settings.",
"custom_drag": "Custom (Hold and Drag)",
"switchToEVMCompatibleWallet": "Please switch to an EVM compatible wallet and try again (Ethereum, Polygon)"
}

View file

@ -760,6 +760,8 @@
"seed_language_chinese_traditional": "Chino (tradicional)",
"polygonscan_history": "Historial de PolygonScan",
"wallet_seed_legacy": "Semilla de billetera heredada",
"default_sell_provider": "Proveedor de venta predeterminado",
"select_sell_provider_notice": "Seleccione un proveedor de venta arriba. Puede omitir esta pantalla configurando su proveedor de venta predeterminado en la configuración de la aplicación.",
"custom_drag": "Custom (mantenía y arrastre)",
"switchToEVMCompatibleWallet": "Cambie a una billetera compatible con EVM e inténtelo nuevamente (Ethereum, Polygon)"
}

View file

@ -760,6 +760,8 @@
"dfx_option_description": "Achetez des crypto-monnaies avec EUR et CHF. Jusqu'à 990€ sans KYC supplémentaire. Pour les clients particuliers et entreprises en Europe",
"polygonscan_history": "Historique de PolygonScan",
"wallet_seed_legacy": "Graine de portefeuille hérité",
"default_sell_provider": "Fournisseur de vente par défaut",
"select_sell_provider_notice": "Sélectionnez un fournisseur de vente ci-dessus. Vous pouvez ignorer cet écran en définissant votre fournisseur de vente par défaut dans les paramètres de l'application.",
"custom_drag": "Custom (maintenir et traîner)",
"switchToEVMCompatibleWallet": "Veuillez passer à un portefeuille compatible EVM et réessayer (Ethereum, Polygon)"
}

View file

@ -742,6 +742,8 @@
"polygonscan_history": "PolygonScan tarihin kowane zamani",
"wallet_seed_legacy": "Tallarin walat walat",
"setup_totp_recommended": "Saita TOTP",
"default_sell_provider": "Tsohuwar Mai Bayar Siyarwa",
"select_sell_provider_notice": "Zaɓi mai bada siyarwa a sama. Kuna iya tsallake wannan allon ta saita mai bada siyar da ku a cikin saitunan app.",
"custom_drag": "Al'ada (riƙe da ja)",
"switchToEVMCompatibleWallet": "Da fatan za a canza zuwa walat ɗin EVM mai jituwa kuma a sake gwadawa (Ethereum, Polygon)"
}

View file

@ -760,6 +760,8 @@
"dfx_option_description": "EUR और CHF के साथ क्रिप्टो खरीदें। अतिरिक्त केवाईसी के बिना 990€ तक। यूरोप में खुदरा और कॉर्पोरेट ग्राहकों के लिए",
"polygonscan_history": "पॉलीगॉनस्कैन इतिहास",
"wallet_seed_legacy": "विरासत बटुए बीज",
"default_sell_provider": "डिफ़ॉल्ट विक्रय प्रदाता",
"select_sell_provider_notice": "ऊपर एक विक्रय प्रदाता का चयन करें। आप ऐप सेटिंग में अपना डिफ़ॉल्ट विक्रय प्रदाता सेट करके इस स्क्रीन को छोड़ सकते हैं।",
"custom_drag": "कस्टम (पकड़ और खींचें)",
"switchToEVMCompatibleWallet": "कृपया ईवीएम संगत वॉलेट पर स्विच करें और पुनः प्रयास करें (एथेरियम, पॉलीगॉन)"
}

View file

@ -758,6 +758,8 @@
"dfx_option_description": "Kupujte kripto s EUR i CHF. Do 990 € bez dodatnog KYC-a. Za maloprodajne i poslovne korisnike u Europi",
"polygonscan_history": "Povijest PolygonScan",
"wallet_seed_legacy": "Sjeme naslijeđenog novčanika",
"default_sell_provider": "Zadani dobavljač prodaje",
"select_sell_provider_notice": "Gore odaberite pružatelja usluga prodaje. Ovaj zaslon možete preskočiti postavljanjem zadanog pružatelja usluga prodaje u postavkama aplikacije.",
"custom_drag": "Prilagođeni (držite i povucite)",
"switchToEVMCompatibleWallet": "Prijeđite na novčanik kompatibilan s EVM-om i pokušajte ponovno (Ethereum, Polygon)"
}

View file

@ -748,6 +748,8 @@
"dfx_option_description": "Beli kripto dengan EUR & CHF. Hingga 990€ tanpa KYC tambahan. Untuk pelanggan ritel dan korporat di Eropa",
"polygonscan_history": "Sejarah PolygonScan",
"wallet_seed_legacy": "Biji dompet warisan",
"default_sell_provider": "Penyedia Penjualan Default",
"select_sell_provider_notice": "Pilih penyedia jual di atas. Anda dapat melewati layar ini dengan mengatur penyedia penjualan default Anda di pengaturan aplikasi.",
"custom_drag": "Khusus (tahan dan seret)",
"switchToEVMCompatibleWallet": "Silakan beralih ke dompet yang kompatibel dengan EVM dan coba lagi (Ethereum, Polygon)"
}

View file

@ -760,6 +760,8 @@
"dfx_option_description": "Acquista criptovalute con EUR e CHF. Fino a 990€ senza KYC aggiuntivi. Per clienti al dettaglio e aziendali in Europa",
"polygonscan_history": "Cronologia PolygonScan",
"wallet_seed_legacy": "Seme di portafoglio legacy",
"default_sell_provider": "Fornitore di vendita predefinito",
"select_sell_provider_notice": "Seleziona un fornitore di vendita sopra. Puoi saltare questa schermata impostando il tuo fornitore di vendita predefinito nelle impostazioni dell'app.",
"custom_drag": "Custom (Hold and Drag)",
"switchToEVMCompatibleWallet": "Passa a un portafoglio compatibile con EVM e riprova (Ethereum, Polygon)"
}

View file

@ -760,6 +760,8 @@
"dfx_option_description": "EUR と CHF で暗号通貨を購入します。追加のKYCなしで最大990ユーロ。ヨーロッパの小売および法人顧客向け",
"polygonscan_history": "ポリゴンスキャン履歴",
"wallet_seed_legacy": "レガシーウォレットシード",
"default_sell_provider": "デフォルトの販売プロバイダー",
"select_sell_provider_notice": "上記の販売プロバイダーを選択してください。アプリ設定でデフォルトの販売プロバイダーを設定することで、この画面をスキップできます。",
"custom_drag": "カスタム(ホールドとドラッグ)",
"switchToEVMCompatibleWallet": "EVM 互換のウォレットに切り替えて再試行してください (イーサリアム、ポリゴン)"
}

View file

@ -758,6 +758,8 @@
"dfx_option_description": "EUR 및 CHF로 암호화폐를 구매하세요. 추가 KYC 없이 최대 990€. 유럽의 소매 및 기업 고객용",
"polygonscan_history": "다각형 스캔 기록",
"wallet_seed_legacy": "레거시 지갑 시드",
"default_sell_provider": "기본 판매 공급자",
"select_sell_provider_notice": "위에서 판매 공급자를 선택하세요. 앱 설정에서 기본 판매 공급자를 설정하면 이 화면을 건너뛸 수 있습니다.",
"custom_drag": "사용자 정의 (홀드 앤 드래그)",
"switchToEVMCompatibleWallet": "EVM 호환 지갑으로 전환 후 다시 시도해 주세요. (이더리움, 폴리곤)"
}

View file

@ -758,6 +758,8 @@
"dfx_option_description": "EUR & CHF ဖြင့် crypto ကိုဝယ်ပါ။ အပို KYC မပါဘဲ 990€ အထိ။ ဥရောပရှိ လက်လီရောင်းချသူများနှင့် ကော်ပိုရိတ်ဖောက်သည်များအတွက်",
"polygonscan_history": "PolygonScan မှတ်တမ်း",
"wallet_seed_legacy": "အမွေအနှစ်ပိုက်ဆံအိတ်မျိုးစေ့",
"default_sell_provider": "ပုံသေရောင်းချပေးသူ",
"select_sell_provider_notice": "အထက်ဖော်ပြပါ အရောင်းဝန်ဆောင်မှုပေးသူကို ရွေးပါ။ အက်ပ်ဆက်တင်များတွင် သင်၏မူလရောင်းချပေးသူကို သတ်မှတ်ခြင်းဖြင့် ဤစခရင်ကို ကျော်နိုင်သည်။",
"custom_drag": "စိတ်ကြိုက် (Drag)",
"switchToEVMCompatibleWallet": "ကျေးဇူးပြု၍ EVM တွဲဖက်သုံးနိုင်သော ပိုက်ဆံအိတ်သို့ ပြောင်းပြီး ထပ်စမ်းကြည့်ပါ (Ethereum၊ Polygon)"
}

View file

@ -760,6 +760,8 @@
"dfx_option_description": "Koop crypto met EUR & CHF. Tot 990€ zonder extra KYC. Voor particuliere en zakelijke klanten in Europa",
"polygonscan_history": "PolygonScan-geschiedenis",
"wallet_seed_legacy": "Legacy portemonnee zaad",
"default_sell_provider": "Standaard verkoopaanbieder",
"select_sell_provider_notice": "Selecteer hierboven een verkoopaanbieder. U kunt dit scherm overslaan door uw standaardverkoopprovider in te stellen in de app-instellingen.",
"custom_drag": "Custom (vasthouden en slepen)",
"switchToEVMCompatibleWallet": "Schakel over naar een EVM-compatibele portemonnee en probeer het opnieuw (Ethereum, Polygon)"
}

View file

@ -760,6 +760,8 @@
"dfx_option_description": "Kupuj kryptowaluty za EUR i CHF. Do 990 € bez dodatkowego KYC. Dla klientów detalicznych i korporacyjnych w Europie",
"polygonscan_history": "Historia PolygonScan",
"wallet_seed_legacy": "Dziedziczne ziarno portfela",
"default_sell_provider": "Domyślny dostawca sprzedaży",
"select_sell_provider_notice": "Wybierz dostawcę sprzedaży powyżej. Możesz pominąć ten ekran, ustawiając domyślnego dostawcę sprzedaży w ustawieniach aplikacji.",
"custom_drag": "Niestandardowe (trzymaj i przeciągnij)",
"switchToEVMCompatibleWallet": "Przejdź na portfel zgodny z EVM i spróbuj ponownie (Ethereum, Polygon)"
}

View file

@ -759,6 +759,8 @@
"dfx_option_description": "Compre criptografia com EUR e CHF. Até 990€ sem KYC adicional. Para clientes de varejo e corporativos na Europa",
"polygonscan_history": "História do PolygonScan",
"wallet_seed_legacy": "Semente de carteira herdada",
"default_sell_provider": "Provedor de venda padrão",
"select_sell_provider_notice": "Selecione um fornecedor de venda acima. Você pode pular esta tela definindo seu provedor de venda padrão nas configurações do aplicativo.",
"custom_drag": "Personalizado (segure e arraste)",
"switchToEVMCompatibleWallet": "Mude para uma carteira compatível com EVM e tente novamente (Ethereum, Polygon)"
}

View file

@ -760,6 +760,8 @@
"dfx_option_description": "Покупайте криптовалюту за EUR и CHF. До 990€ без дополнительного KYC. Для розничных и корпоративных клиентов в Европе",
"polygonscan_history": "История PolygonScan",
"wallet_seed_legacy": "Наследие семя кошелька",
"default_sell_provider": "Поставщик продаж по умолчанию",
"select_sell_provider_notice": "Выберите поставщика услуг продажи выше. Вы можете пропустить этот экран, установив поставщика услуг продаж по умолчанию в настройках приложения.",
"custom_drag": "Пользователь (удерживайте и перетаскивайте)",
"switchToEVMCompatibleWallet": "Пожалуйста, переключитесь на кошелек, совместимый с EVM, и повторите попытку (Ethereum, Polygon)."
}

View file

@ -758,6 +758,8 @@
"dfx_option_description": "ซื้อ crypto ด้วย EUR และ CHF สูงถึง 990€ โดยไม่มี KYC เพิ่มเติม สำหรับลูกค้ารายย่อยและลูกค้าองค์กรในยุโรป",
"polygonscan_history": "ประวัติ PolygonScan",
"wallet_seed_legacy": "เมล็ดกระเป๋าเงินมรดก",
"default_sell_provider": "ผู้ให้บริการการขายเริ่มต้น",
"select_sell_provider_notice": "เลือกผู้ให้บริการการขายด้านบน คุณสามารถข้ามหน้าจอนี้ได้โดยการตั้งค่าผู้ให้บริการการขายเริ่มต้นในการตั้งค่าแอป",
"custom_drag": "กำหนดเอง (ค้างและลาก)",
"switchToEVMCompatibleWallet": "โปรดเปลี่ยนไปใช้กระเป๋าเงินที่รองรับ EVM แล้วลองอีกครั้ง (Ethereum, Polygon)"
}

View file

@ -754,6 +754,8 @@
"dfx_option_description": "Bumili ng crypto gamit ang EUR at CHF. Hanggang 990€ nang walang karagdagang KYC. Para sa retail at corporate na mga customer sa Europe",
"polygonscan_history": "Kasaysayan ng PolygonScan",
"wallet_seed_legacy": "Legacy wallet seed",
"default_sell_provider": "Default na Sell Provider",
"select_sell_provider_notice": "Pumili ng provider ng nagbebenta sa itaas. Maaari mong laktawan ang screen na ito sa pamamagitan ng pagtatakda ng iyong default na sell provider sa mga setting ng app.",
"custom_drag": "Pasadyang (hawakan at i -drag)",
"switchToEVMCompatibleWallet": "Mangyaring lumipat sa isang EVM compatible na wallet at subukang muli (Ethereum, Polygon)"
}

View file

@ -758,6 +758,8 @@
"dfx_option_description": "EUR ve CHF ile kripto satın alın. Ek KYC olmadan 990 €'ya kadar. Avrupa'daki perakende ve kurumsal müşteriler için",
"polygonscan_history": "PolygonScan geçmişi",
"wallet_seed_legacy": "Eski cüzdan tohumu",
"default_sell_provider": "Varsayılan Satış Sağlayıcısı",
"select_sell_provider_notice": "Yukarıdan bir satış sağlayıcısı seçin. Uygulama ayarlarında varsayılan satış sağlayıcınızı ayarlayarak bu ekranı atlayabilirsiniz.",
"custom_drag": "Özel (Bekle ve Sürükle)",
"switchToEVMCompatibleWallet": "Lütfen EVM uyumlu bir cüzdana geçin ve tekrar deneyin (Ethereum, Polygon)"
}

View file

@ -760,6 +760,8 @@
"seed_language_chinese_traditional": "Китайський (традиційний)",
"polygonscan_history": "Історія PolygonScan",
"wallet_seed_legacy": "Спадець насіння гаманця",
"default_sell_provider": "Постачальник продажу за замовчуванням",
"select_sell_provider_notice": "Виберіть вище постачальника послуг продажу. Ви можете пропустити цей екран, встановивши постачальника послуг продажу за умовчанням у налаштуваннях програми.",
"custom_drag": "На замовлення (утримуйте та перетягується)",
"switchToEVMCompatibleWallet": "Перейдіть на гаманець, сумісний з EVM, і повторіть спробу (Ethereum, Polygon)"
}

View file

@ -752,6 +752,8 @@
"dfx_option_description": "EUR ﺭﻭﺍ CHF ﯽﻓﺎﺿﺍ ۔ﮟﯾﺪﯾﺮﺧ ﻮﭩﭘﺮﮐ ﮫﺗﺎﺳ ﮯﮐ KYC ﮯﯿﻟ ﮯﮐ ﻦﯿﻓﺭﺎﺻ ﭧﯾﺭﻮﭘﺭﺎﮐ ﺭﻭﺍ ﮦﺩﺭﻮﺧ ﮟ",
"polygonscan_history": "ﺦﯾﺭﺎﺗ ﯽﮐ ﻦﯿﮑﺳﺍ ﻥﻮﮔ ﯽﻟﻮﭘ",
"wallet_seed_legacy": "میراثی پرس کا بیج",
"default_sell_provider": " ﮦﺪﻨﻨﮐ ﻢﮨﺍﺮﻓ ﻞﯿﺳ ﭧﻟﺎﻔﯾﮈ",
"select_sell_provider_notice": "۔ﮟﯿﮨ ﮯﺘﮑﺳ ﮌﻮﮭﭼ ﻮﮐ ﻦﯾﺮﮑﺳﺍ ﺱﺍ ﺮﮐ ﮮﺩ ﺐﯿﺗﺮﺗ ﻮﮐ ﮦﺪﻨﻨﮐ ﻢﮨﺍﺮﻓ ﻞﯿﺳ ﭧﻟﺎﻔﯾﮈ ﮯﻨﭘﺍ ﮟﯿﻣ ﺕﺎﺒ",
"custom_drag": "کسٹم (ہولڈ اینڈ ڈریگ)",
"switchToEVMCompatibleWallet": "(Ethereum, Polygon) ﮟﯾﺮﮐ ﺶﺷﻮﮐ ﮦﺭﺎﺑﻭﺩ ﺭﻭﺍ ﮟﯾﺮﮐ ﭻﺋﻮﺳ ﺮﭘ ﭧﯿﻟﺍﻭ ﮯﻟﺍﻭ ﮯﻨﮭﮐﺭ ﺖﻘﺑﺎﻄﻣ "
}

View file

@ -754,6 +754,8 @@
"dfx_option_description": "Ra crypto pẹlu EUR & CHF. Titi di 990 € laisi afikun KYC. Fun soobu ati awọn onibara ile-iṣẹ ni Yuroopu",
"polygonscan_history": "PolygonScan itan",
"wallet_seed_legacy": "Irugbin akole",
"default_sell_provider": "Aiyipada Olupese Tita",
"select_sell_provider_notice": "Yan olupese ti o ta loke. O le foju iboju yii nipa tito olupese iṣẹ tita aiyipada rẹ ni awọn eto app.",
"custom_drag": "Aṣa (mu ati fa)",
"switchToEVMCompatibleWallet": "Jọwọ yipada si apamọwọ ibaramu EVM ki o tun gbiyanju lẹẹkansi (Ethereum, Polygon)"
}

View file

@ -759,6 +759,8 @@
"dfx_option_description": "用欧元和瑞士法郎购买加密货币。高达 990 欧元,无需额外 KYC。对于欧洲的零售和企业客户",
"polygonscan_history": "多边形扫描历史",
"wallet_seed_legacy": "旧的钱包种子",
"default_sell_provider": "默认销售提供商",
"select_sell_provider_notice": "选择上面的销售提供商。您可以通过在应用程序设置中设置默认销售提供商来跳过此屏幕。",
"custom_drag": "定制(保持和拖动)",
"switchToEVMCompatibleWallet": "请切换到 EVM 兼容钱包并重试以太坊、Polygon"
}

View file

@ -15,15 +15,15 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN)
APP_ANDROID_TYPE=$1
MONERO_COM_NAME="Monero.com"
MONERO_COM_VERSION="1.8.0"
MONERO_COM_BUILD_NUMBER=69
MONERO_COM_VERSION="1.9.0"
MONERO_COM_BUILD_NUMBER=71
MONERO_COM_BUNDLE_ID="com.monero.app"
MONERO_COM_PACKAGE="com.monero.app"
MONERO_COM_SCHEME="monero.com"
CAKEWALLET_NAME="Cake Wallet"
CAKEWALLET_VERSION="4.11.0"
CAKEWALLET_BUILD_NUMBER=184
CAKEWALLET_VERSION="4.12.1"
CAKEWALLET_BUILD_NUMBER=188
CAKEWALLET_BUNDLE_ID="com.cakewallet.cake_wallet"
CAKEWALLET_PACKAGE="com.cakewallet.cake_wallet"
CAKEWALLET_SCHEME="cakewallet"

View file

@ -13,13 +13,13 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN)
APP_IOS_TYPE=$1
MONERO_COM_NAME="Monero.com"
MONERO_COM_VERSION="1.8.0"
MONERO_COM_BUILD_NUMBER=67
MONERO_COM_VERSION="1.9.0"
MONERO_COM_BUILD_NUMBER=69
MONERO_COM_BUNDLE_ID="com.cakewallet.monero"
CAKEWALLET_NAME="Cake Wallet"
CAKEWALLET_VERSION="4.11.0"
CAKEWALLET_BUILD_NUMBER=202
CAKEWALLET_VERSION="4.12.1"
CAKEWALLET_BUILD_NUMBER=206
CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet"
HAVEN_NAME="Haven"

View file

@ -15,8 +15,8 @@ if [ -n "$1" ]; then
fi
CAKEWALLET_NAME="Cake Wallet"
CAKEWALLET_VERSION="1.4.0"
CAKEWALLET_BUILD_NUMBER=45
CAKEWALLET_VERSION="1.5.1"
CAKEWALLET_BUILD_NUMBER=48
CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet"
if ! [[ " ${TYPES[*]} " =~ " ${APP_MACOS_TYPE} " ]]; then