mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2024-10-31 17:37:41 +00:00
5c9f176d18
* fix: Improve exchange flow by adding a timeout to the call to fetch rate from providers * fix: Adjust time limit for fetching rate to 7 seconds and add timelimit to fetching limits * fix: Make fetch limits a Future.wait * feat: Add currency for amount and estimated receive amount to confirm sending page for exchange * fix: Remove unneeded code * fix: Modify receive amount to reflect value coming from the individual exchange providers if available and ensure receiveAmount is calculated based on selected exchange provider's rate
442 lines
14 KiB
Dart
442 lines
14 KiB
Dart
import 'dart:io';
|
|
import 'dart:convert';
|
|
import 'package:cake_wallet/core/secure_storage.dart';
|
|
import 'package:collection/collection.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
import 'package:hive/hive.dart';
|
|
import 'package:path_provider/path_provider.dart';
|
|
import 'package:cake_wallet/core/key_service.dart';
|
|
import 'package:cake_wallet/entities/contact.dart';
|
|
import 'package:cw_core/crypto_currency.dart';
|
|
import 'package:cake_wallet/entities/encrypt.dart';
|
|
import 'package:cake_wallet/entities/fiat_currency.dart';
|
|
import 'package:cake_wallet/entities/ios_legacy_helper.dart' as ios_legacy_helper;
|
|
import 'package:cake_wallet/entities/preferences_key.dart';
|
|
import 'package:cake_wallet/entities/secret_store_key.dart';
|
|
import 'package:cw_core/wallet_info.dart';
|
|
import 'package:cw_core/wallet_type.dart';
|
|
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
|
|
import 'package:cake_wallet/exchange/trade.dart';
|
|
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
|
|
|
const reservedNames = ["flutter_assets", "wallets", "db"];
|
|
|
|
Future<void> migrate_android_v1() async {
|
|
final appDocDir = await getApplicationDocumentsDirectory();
|
|
|
|
await android_migrate_hives(appDocDir: appDocDir);
|
|
await android_migrate_wallets(appDocDir: appDocDir);
|
|
}
|
|
|
|
Future<void> ios_migrate_v1(
|
|
Box<WalletInfo> walletInfoSource, Box<Trade> tradeSource, Box<Contact> contactSource) async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
|
|
if (prefs.getBool('ios_migration_v1_completed') ?? false) {
|
|
return;
|
|
}
|
|
|
|
await ios_migrate_user_defaults();
|
|
await ios_migrate_pin();
|
|
await ios_migrate_wallet_passwords();
|
|
await ios_migrate_wallet_info(walletInfoSource);
|
|
await ios_migrate_trades_list(tradeSource);
|
|
await ios_migrate_address_book(contactSource);
|
|
|
|
await prefs.setBool('ios_migration_v1_completed', true);
|
|
}
|
|
|
|
Future<void> ios_migrate_user_defaults() async {
|
|
//get the new shared preferences instance
|
|
final prefs = await SharedPreferences.getInstance();
|
|
|
|
if (prefs.getBool('ios_migration_user_defaults_completed') ?? false) {
|
|
return;
|
|
}
|
|
|
|
//translate the node uri
|
|
final nodeURI = await ios_legacy_helper.getString('node_uri');
|
|
// await prefs.setString('current_node_id', nodeURI);
|
|
await prefs.setInt('current_node_id', 0);
|
|
|
|
//should we provide default btc node key?
|
|
final activeCurrency = await ios_legacy_helper.getInt('currency');
|
|
|
|
if (activeCurrency != null) {
|
|
final convertedCurrency = convertFiatLegacy(activeCurrency);
|
|
|
|
await prefs.setString('current_fiat_currency', convertedCurrency.serialize());
|
|
}
|
|
|
|
//translate fee priority
|
|
final activeFeeTier = await ios_legacy_helper.getInt('saved_fee_priority');
|
|
|
|
if (activeFeeTier != null) {
|
|
await prefs.setInt('current_fee_priority', activeFeeTier);
|
|
}
|
|
|
|
//translate current balance mode
|
|
final currentBalanceMode = await ios_legacy_helper.getInt('display_balance_mode');
|
|
if (currentBalanceMode != null) {
|
|
await prefs.setInt('current_balance_display_mode', currentBalanceMode);
|
|
}
|
|
|
|
//translate should save recipient address
|
|
final shouldSave = await ios_legacy_helper.getBool('should_save_recipient_address');
|
|
|
|
if (shouldSave != null) {
|
|
await prefs.setBool('save_recipient_address', shouldSave);
|
|
}
|
|
|
|
//translate biometric
|
|
final biometricOn = await ios_legacy_helper.getBool('biometric_authentication_on');
|
|
|
|
if (biometricOn != null) {
|
|
await prefs.setBool('allow_biometrical_authentication', biometricOn);
|
|
}
|
|
|
|
//read the current theme as integer, write it back as a bool
|
|
final currentTheme = prefs.getInt('current-theme');
|
|
bool isDark = false;
|
|
if (currentTheme == 1) {
|
|
isDark = true;
|
|
}
|
|
await prefs.setBool('dark_theme', isDark);
|
|
|
|
//assign the pin length
|
|
final pinLength = await ios_legacy_helper.getInt('pin-length');
|
|
|
|
if (pinLength != null) {
|
|
await prefs.setInt(PreferencesKey.currentPinLength, pinLength);
|
|
}
|
|
|
|
//default value for display list key?
|
|
final walletName = await ios_legacy_helper.getString('current_wallet_name');
|
|
|
|
if (walletName != null) {
|
|
await prefs.setString('current_wallet_name', walletName);
|
|
}
|
|
|
|
await prefs.setInt('current_wallet_type', serializeToInt(WalletType.monero));
|
|
|
|
await prefs.setBool('ios_migration_user_defaults_completed', true);
|
|
}
|
|
|
|
Future<void> ios_migrate_pin() async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
|
|
if (prefs.getBool('ios_migration_pin_completed') ?? false) {
|
|
return;
|
|
}
|
|
|
|
final flutterSecureStorage = secureStorageShared;
|
|
final pinPassword = await flutterSecureStorage.readNoIOptions(key: 'pin_password');
|
|
// No pin
|
|
if (pinPassword == null) {
|
|
await prefs.setBool('ios_migration_pin_completed', true);
|
|
return;
|
|
}
|
|
|
|
final key = generateStoreKeyFor(key: SecretStoreKey.pinCodePassword);
|
|
final encodedPassword = encodedPinCode(pin: pinPassword);
|
|
await flutterSecureStorage.write(key: key, value: encodedPassword);
|
|
|
|
await prefs.setBool('ios_migration_pin_completed', true);
|
|
}
|
|
|
|
Future<void> ios_migrate_wallet_passwords() async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
|
|
if (prefs.getBool('ios_migration_wallet_passwords_completed') ?? false) {
|
|
return;
|
|
}
|
|
|
|
final appDocDir = await getApplicationDocumentsDirectory();
|
|
final flutterSecureStorage = secureStorageShared;
|
|
final keyService = KeyService(flutterSecureStorage);
|
|
final walletsDir = Directory('${appDocDir.path}/wallets');
|
|
final moneroWalletsDir = Directory('${walletsDir.path}/monero');
|
|
|
|
if (!moneroWalletsDir.existsSync() || moneroWalletsDir.listSync().isEmpty) {
|
|
await prefs.setBool('ios_migration_wallet_passwords_completed', true);
|
|
return;
|
|
}
|
|
|
|
moneroWalletsDir.listSync().forEach((item) async {
|
|
try {
|
|
if (item is Directory) {
|
|
final name = item.path.split('/').last;
|
|
final oldKey = 'wallet_monero_' + name + '_password';
|
|
final password = await flutterSecureStorage.readNoIOptions(key: oldKey);
|
|
await keyService.saveWalletPassword(walletName: name, password: password!);
|
|
}
|
|
} catch (e) {
|
|
print(e.toString());
|
|
}
|
|
});
|
|
|
|
await prefs.setBool('ios_migration_wallet_passwords_completed', true);
|
|
}
|
|
|
|
FiatCurrency convertFiatLegacy(int raw) {
|
|
final _map = {
|
|
0: 'aud',
|
|
1: 'bgn',
|
|
2: 'brl',
|
|
3: 'cad',
|
|
4: 'chf',
|
|
5: 'cny',
|
|
6: 'czk',
|
|
7: 'eur',
|
|
8: 'dkk',
|
|
9: 'gbp',
|
|
10: 'hkd',
|
|
11: 'hrk',
|
|
12: 'huf',
|
|
13: 'idr',
|
|
14: 'ils',
|
|
15: 'inr',
|
|
16: 'isk',
|
|
17: 'jpy',
|
|
18: 'krw',
|
|
19: 'mxn',
|
|
20: 'myr',
|
|
21: 'nok',
|
|
22: 'nzd',
|
|
23: 'php',
|
|
24: 'pln',
|
|
25: 'ron',
|
|
26: 'rub',
|
|
27: 'sek',
|
|
28: 'sgd',
|
|
29: 'thb',
|
|
30: 'try',
|
|
31: 'usd',
|
|
32: 'zar',
|
|
33: 'vef'
|
|
};
|
|
final fiatAsString = _map[raw]!;
|
|
|
|
return FiatCurrency.deserialize(raw: fiatAsString.toUpperCase());
|
|
}
|
|
|
|
Future<void> android_migrate_hives({required Directory appDocDir}) async {
|
|
final dbDir = Directory('${appDocDir.path}/db');
|
|
final files = <File>[];
|
|
|
|
appDocDir.listSync().forEach((FileSystemEntity item) {
|
|
final ext = item.path.split('.').last;
|
|
|
|
if (item is File && (ext == "hive" || ext == "lock")) {
|
|
files.add(item);
|
|
}
|
|
});
|
|
|
|
if (!dbDir.existsSync()) {
|
|
dbDir.createSync();
|
|
}
|
|
|
|
files.forEach((File hive) {
|
|
final name = hive.path.split('/').last;
|
|
hive.copySync('${dbDir.path}/$name');
|
|
hive.deleteSync();
|
|
});
|
|
}
|
|
|
|
Future<void> android_migrate_wallets({required Directory appDocDir}) async {
|
|
final walletsDir = Directory('${appDocDir.path}/wallets');
|
|
final moneroWalletsDir = Directory('${walletsDir.path}/monero');
|
|
final dirs = <Directory>[];
|
|
|
|
appDocDir.listSync().forEach((FileSystemEntity item) {
|
|
final name = item.path.split('/').last;
|
|
|
|
if (item is Directory && !reservedNames.contains(name)) {
|
|
dirs.add(item);
|
|
}
|
|
});
|
|
|
|
if (!moneroWalletsDir.existsSync()) {
|
|
await moneroWalletsDir.create(recursive: true);
|
|
}
|
|
|
|
dirs.forEach((Directory dir) {
|
|
final name = dir.path.split('/').last;
|
|
final newDir = Directory('${moneroWalletsDir.path}/$name');
|
|
newDir.createSync();
|
|
|
|
dir.listSync().forEach((file) {
|
|
if (file is File) {
|
|
final fileName = file.path.split('/').last;
|
|
file.copySync('${newDir.path}/$fileName');
|
|
file.deleteSync();
|
|
}
|
|
});
|
|
|
|
dir.deleteSync();
|
|
});
|
|
}
|
|
|
|
Future<void> ios_migrate_wallet_info(Box<WalletInfo> walletsInfoSource) async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
|
|
if (prefs.getBool('ios_migration_wallet_info_completed') ?? false) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
final appDocDir = await getApplicationDocumentsDirectory();
|
|
final walletsDir = Directory('${appDocDir.path}/wallets');
|
|
final moneroWalletsDir = Directory('${walletsDir.path}/monero');
|
|
final infoRecords = moneroWalletsDir
|
|
.listSync()
|
|
.map((item) {
|
|
try {
|
|
if (item is Directory) {
|
|
final name = item.path.split('/').last;
|
|
final configFile = File('${item.path}/$name.json');
|
|
|
|
if (!configFile.existsSync()) {
|
|
return null;
|
|
}
|
|
|
|
final config = json.decode(configFile.readAsStringSync()) as Map<String, dynamic>;
|
|
final isRecovery = config['isRecovery'] as bool? ?? false;
|
|
final dateAsDouble = config['date'] as double;
|
|
final timestamp = dateAsDouble.toInt() * 1000;
|
|
final date = DateTime.fromMillisecondsSinceEpoch(timestamp);
|
|
final id = walletTypeToString(WalletType.monero).toLowerCase() + '_' + name;
|
|
final exist = walletsInfoSource.values.firstWhereOrNull((el) => el.id == id) != null;
|
|
|
|
if (exist) {
|
|
return null;
|
|
}
|
|
|
|
final walletInfo = WalletInfo.external(
|
|
id: id,
|
|
type: WalletType.monero,
|
|
name: name,
|
|
isRecovery: isRecovery,
|
|
restoreHeight: 0,
|
|
date: date,
|
|
dirPath: item.path,
|
|
path: '${item.path}/$name',
|
|
address: '');
|
|
|
|
return walletInfo;
|
|
}
|
|
} catch (e) {
|
|
print(e.toString());
|
|
return null;
|
|
}
|
|
})
|
|
.where((el) => el != null)
|
|
.whereType<WalletInfo>()
|
|
.toList();
|
|
await walletsInfoSource.addAll(infoRecords);
|
|
await prefs.setBool('ios_migration_wallet_info_completed', true);
|
|
} catch (e) {
|
|
print(e.toString());
|
|
}
|
|
}
|
|
|
|
Future<void> ios_migrate_trades_list(Box<Trade> tradeSource) async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
|
|
if (prefs.getBool('ios_migration_trade_list_completed') ?? false) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
final appDocDir = await getApplicationDocumentsDirectory();
|
|
final url = '${appDocDir.path}/trades_list.json';
|
|
final file = File(url);
|
|
|
|
if (!file.existsSync()) {
|
|
await prefs.setBool('ios_migration_trade_list_completed', true);
|
|
return;
|
|
}
|
|
|
|
final content = file.readAsBytesSync();
|
|
final flutterSecureStorage = secureStorageShared;
|
|
final masterPassword = await flutterSecureStorage.readNoIOptions(key: 'master_password');
|
|
final key = masterPassword!.replaceAll('-', '');
|
|
final decoded = await ios_legacy_helper.decrypt(content, key: key, salt: secrets.salt);
|
|
final decodedJson = json.decode(decoded) as List<dynamic>;
|
|
final trades = decodedJson.map((dynamic el) {
|
|
final elAsMap = el as Map<String, dynamic>;
|
|
final providerAsString = elAsMap['provider'] as String;
|
|
final fromAsString = elAsMap['from'] as String;
|
|
final toAsString = elAsMap['to'] as String;
|
|
final dateAsDouble = elAsMap['date'] as double;
|
|
final tradeId = elAsMap['tradeID'] as String;
|
|
final to = CryptoCurrency.fromString(toAsString);
|
|
final from = CryptoCurrency.fromString(fromAsString);
|
|
final timestamp = dateAsDouble.toInt() * 1000;
|
|
final date = DateTime.fromMillisecondsSinceEpoch(timestamp);
|
|
ExchangeProviderDescription? provider;
|
|
|
|
switch (providerAsString.toLowerCase()) {
|
|
case 'changenow':
|
|
provider = ExchangeProviderDescription.changeNow;
|
|
break;
|
|
case 'xmr.to':
|
|
provider = ExchangeProviderDescription.xmrto;
|
|
break;
|
|
case 'morph':
|
|
provider = ExchangeProviderDescription.morphToken;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return Trade(
|
|
id: tradeId,
|
|
provider: provider!,
|
|
from: from,
|
|
to: to,
|
|
createdAt: date,
|
|
amount: '',
|
|
receiveAmount: '',
|
|
);
|
|
});
|
|
await tradeSource.addAll(trades);
|
|
await prefs.setBool('ios_migration_trade_list_completed', true);
|
|
} catch (e) {
|
|
print(e.toString());
|
|
}
|
|
}
|
|
|
|
Future<void> ios_migrate_address_book(Box<Contact> contactSource) async {
|
|
try {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
|
|
if (prefs.getBool('ios_migration_address_book_completed') ?? false) {
|
|
return;
|
|
}
|
|
|
|
final appDocDir = await getApplicationDocumentsDirectory();
|
|
final addressBookJSON = File('${appDocDir.path}/address_book.json');
|
|
|
|
if (!addressBookJSON.existsSync()) {
|
|
await prefs.setBool('ios_migration_address_book_completed', true);
|
|
return;
|
|
}
|
|
|
|
final List<dynamic> addresses =
|
|
json.decode(addressBookJSON.readAsStringSync()) as List<dynamic>;
|
|
final contacts = addresses.map((dynamic item) {
|
|
final _item = item as Map<String, dynamic>;
|
|
final type = _item["type"] as String;
|
|
final address = _item["address"] as String;
|
|
final name = _item["name"] as String;
|
|
|
|
return Contact(address: address, name: name, type: CryptoCurrency.fromString(type));
|
|
});
|
|
|
|
await contactSource.addAll(contacts);
|
|
await prefs.setBool('ios_migration_address_book_completed', true);
|
|
} catch (e) {
|
|
print(e.toString());
|
|
}
|
|
}
|