mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-20 06:08:34 +02:00
Part 1
This commit is contained in:
@@ -1,9 +0,0 @@
|
||||
import 'package:cake_wallet/src/domain/common/crypto_amount_format.dart';
|
||||
|
||||
const bitcoinAmountDivider = 100000000;
|
||||
|
||||
double bitcoinAmountToDouble({int amount}) =>
|
||||
cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider);
|
||||
|
||||
int doubleToBitcoinAmount(double amount) =>
|
||||
(amount * bitcoinAmountDivider).toInt();
|
||||
@@ -1,6 +0,0 @@
|
||||
import 'package:cake_wallet/src/domain/common/crypto_amount_format.dart';
|
||||
|
||||
const bitcoinCashAmountDivider = 100000000;
|
||||
|
||||
double bitcoinCashAmountToDouble({int amount}) =>
|
||||
cryptoAmountToDouble(amount: amount, divider: bitcoinCashAmountDivider);
|
||||
@@ -1,3 +0,0 @@
|
||||
abstract class Balance {
|
||||
const Balance();
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/domain/common/enumerable_item.dart';
|
||||
|
||||
class BalanceDisplayMode extends EnumerableItem<int> with Serializable<int> {
|
||||
const BalanceDisplayMode({@required String title, @required int raw})
|
||||
: super(title: title, raw: raw);
|
||||
|
||||
static const all = [
|
||||
BalanceDisplayMode.fullBalance,
|
||||
BalanceDisplayMode.availableBalance,
|
||||
BalanceDisplayMode.hiddenBalance
|
||||
];
|
||||
static const fullBalance = BalanceDisplayMode(raw: 0, title: 'Full Balance');
|
||||
static const availableBalance =
|
||||
BalanceDisplayMode(raw: 1, title: 'Available Balance');
|
||||
static const hiddenBalance =
|
||||
BalanceDisplayMode(raw: 2, title: 'Hidden Balance');
|
||||
|
||||
static BalanceDisplayMode deserialize({int raw}) {
|
||||
switch (raw) {
|
||||
case 0:
|
||||
return fullBalance;
|
||||
case 1:
|
||||
return availableBalance;
|
||||
case 2:
|
||||
return hiddenBalance;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
switch (this) {
|
||||
case BalanceDisplayMode.fullBalance:
|
||||
return S.current.xmr_full_balance;
|
||||
case BalanceDisplayMode.availableBalance:
|
||||
return S.current.xmr_available_balance;
|
||||
case BalanceDisplayMode.hiddenBalance:
|
||||
return S.current.xmr_hidden;
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
import 'package:local_auth/local_auth.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
|
||||
class BiometricAuth {
|
||||
final _localAuth = LocalAuthentication();
|
||||
|
||||
Future<bool> isAuthenticated() async {
|
||||
try {
|
||||
return await _localAuth.authenticateWithBiometrics(
|
||||
localizedReason: S.current.biometric_auth_reason,
|
||||
useErrorDialogs: true,
|
||||
stickyAuth: false);
|
||||
} on PlatformException catch (e) {
|
||||
print(e);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Future<bool> canCheckBiometrics() async {
|
||||
try {
|
||||
return await _localAuth.canCheckBiometrics;
|
||||
} on PlatformException catch (e) {
|
||||
print(e);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import 'package:cake_wallet/src/domain/common/transaction_priority.dart';
|
||||
|
||||
double calculateEstimatedFee({TransactionPriority priority}) {
|
||||
if (priority == TransactionPriority.slow) {
|
||||
return 0.00002459;
|
||||
}
|
||||
|
||||
if (priority == TransactionPriority.regular) {
|
||||
return 0.00012305;
|
||||
}
|
||||
|
||||
if (priority == TransactionPriority.medium) {
|
||||
return 0.00024503;
|
||||
}
|
||||
|
||||
if (priority == TransactionPriority.fast) {
|
||||
return 0.00061453;
|
||||
}
|
||||
|
||||
if (priority == TransactionPriority.fastest) {
|
||||
return 0.0260216;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
String calculateFiatAmount({double price, String cryptoAmount}) {
|
||||
if (price == null || cryptoAmount == null) {
|
||||
return '0.00';
|
||||
}
|
||||
|
||||
final _amount = double.parse(cryptoAmount);
|
||||
final result = price * _amount;
|
||||
|
||||
if (result == 0.0) {
|
||||
return '0.00';
|
||||
}
|
||||
|
||||
return result > 0.01 ? result.toStringAsFixed(2) : '< 0.01';
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
String calculateFiatAmountRaw({double price, double cryptoAmount}) {
|
||||
if (price == null) {
|
||||
return '0.00';
|
||||
}
|
||||
|
||||
final result = price * cryptoAmount;
|
||||
|
||||
if (result == 0.0) {
|
||||
return '0.00';
|
||||
}
|
||||
|
||||
return result > 0.01 ? result.toStringAsFixed(2) : '< 0.01';
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
|
||||
import 'package:cake_wallet/utils/mobx.dart';
|
||||
|
||||
part 'contact.g.dart';
|
||||
|
||||
@HiveType(typeId: 0)
|
||||
class Contact extends HiveObject with Keyable {
|
||||
Contact({@required this.name, @required this.address, CryptoCurrency type})
|
||||
: raw = type?.raw;
|
||||
|
||||
static const boxName = 'Contacts';
|
||||
|
||||
@HiveField(0)
|
||||
String name;
|
||||
|
||||
@HiveField(1)
|
||||
String address;
|
||||
|
||||
@HiveField(2)
|
||||
int raw;
|
||||
|
||||
CryptoCurrency get type => CryptoCurrency.deserialize(raw: raw);
|
||||
|
||||
@override
|
||||
dynamic get keyIndex => key;
|
||||
|
||||
@override
|
||||
bool operator ==(Object o) => o is Contact && o.key == key;
|
||||
|
||||
@override
|
||||
int get hashCode => key.hashCode;
|
||||
|
||||
void updateCryptoCurrency({@required CryptoCurrency currency}) =>
|
||||
raw = currency.raw;
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
// import 'package:hive/hive.dart';
|
||||
// import 'package:mobx/mobx.dart';
|
||||
// import 'package:cake_wallet/src/domain/common/contact.dart';
|
||||
// import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
|
||||
|
||||
// part 'contact_model.g.dart';
|
||||
|
||||
// class ContactModel = ContactModelBase with _$ContactModel;
|
||||
|
||||
// abstract class ContactModelBase with Store {
|
||||
// ContactModelBase(this._contacts, {Contact contact}) : _contact = contact {
|
||||
// name = _contact?.name;
|
||||
// address = _contact?.address;
|
||||
// currency = _contact?.type;
|
||||
|
||||
// _contacts.watch(key: contact.key).listen((event) {
|
||||
|
||||
// });
|
||||
// }
|
||||
|
||||
// @observable
|
||||
// String name;
|
||||
|
||||
// @observable
|
||||
// String address;
|
||||
|
||||
// @observable
|
||||
// CryptoCurrency currency;
|
||||
|
||||
// // @computed
|
||||
// // bool get isReady =>
|
||||
// // (name?.isNotEmpty ?? false) &&
|
||||
// // (currency?.toString()?.isNotEmpty ?? false) &&
|
||||
// // (address?.isNotEmpty ?? false);
|
||||
|
||||
// final Box<ContactBase> _contacts;
|
||||
// final Contact _contact;
|
||||
// }
|
||||
@@ -1 +0,0 @@
|
||||
double cryptoAmountToDouble({num amount, num divider}) => amount / divider;
|
||||
@@ -1,114 +0,0 @@
|
||||
import 'package:cake_wallet/src/domain/common/enumerable_item.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
part 'crypto_currency.g.dart';
|
||||
|
||||
@HiveType(typeId: 0)
|
||||
class CryptoCurrency extends EnumerableItem<int> with Serializable<int> {
|
||||
const CryptoCurrency({final String title, final int raw})
|
||||
: super(title: title, raw: raw);
|
||||
|
||||
static const all = [
|
||||
CryptoCurrency.xmr,
|
||||
CryptoCurrency.ada,
|
||||
CryptoCurrency.bch,
|
||||
CryptoCurrency.bnb,
|
||||
CryptoCurrency.btc,
|
||||
CryptoCurrency.dash,
|
||||
CryptoCurrency.eos,
|
||||
CryptoCurrency.eth,
|
||||
CryptoCurrency.ltc,
|
||||
CryptoCurrency.nano,
|
||||
CryptoCurrency.trx,
|
||||
CryptoCurrency.usdt,
|
||||
CryptoCurrency.xlm,
|
||||
CryptoCurrency.xrp
|
||||
];
|
||||
static const xmr = CryptoCurrency(title: 'XMR', raw: 0);
|
||||
static const ada = CryptoCurrency(title: 'ADA', raw: 1);
|
||||
static const bch = CryptoCurrency(title: 'BCH', raw: 2);
|
||||
static const bnb = CryptoCurrency(title: 'BNB', raw: 3);
|
||||
static const btc = CryptoCurrency(title: 'BTC', raw: 4);
|
||||
static const dash = CryptoCurrency(title: 'DASH', raw: 5);
|
||||
static const eos = CryptoCurrency(title: 'EOS', raw: 6);
|
||||
static const eth = CryptoCurrency(title: 'ETH', raw: 7);
|
||||
static const ltc = CryptoCurrency(title: 'LTC', raw: 8);
|
||||
static const nano = CryptoCurrency(title: 'NANO', raw: 9);
|
||||
static const trx = CryptoCurrency(title: 'TRX', raw: 10);
|
||||
static const usdt = CryptoCurrency(title: 'USDT', raw: 11);
|
||||
static const xlm = CryptoCurrency(title: 'XLM', raw: 12);
|
||||
static const xrp = CryptoCurrency(title: 'XRP', raw: 13);
|
||||
|
||||
static CryptoCurrency deserialize({int raw}) {
|
||||
switch (raw) {
|
||||
case 0:
|
||||
return CryptoCurrency.xmr;
|
||||
case 1:
|
||||
return CryptoCurrency.ada;
|
||||
case 2:
|
||||
return CryptoCurrency.bch;
|
||||
case 3:
|
||||
return CryptoCurrency.bnb;
|
||||
case 4:
|
||||
return CryptoCurrency.btc;
|
||||
case 5:
|
||||
return CryptoCurrency.dash;
|
||||
case 6:
|
||||
return CryptoCurrency.eos;
|
||||
case 7:
|
||||
return CryptoCurrency.eth;
|
||||
case 8:
|
||||
return CryptoCurrency.ltc;
|
||||
case 9:
|
||||
return CryptoCurrency.nano;
|
||||
case 10:
|
||||
return CryptoCurrency.trx;
|
||||
case 11:
|
||||
return CryptoCurrency.usdt;
|
||||
case 12:
|
||||
return CryptoCurrency.xlm;
|
||||
case 13:
|
||||
return CryptoCurrency.xrp;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static CryptoCurrency fromString(String raw) {
|
||||
switch (raw.toLowerCase()) {
|
||||
case 'xmr':
|
||||
return CryptoCurrency.xmr;
|
||||
case 'ada':
|
||||
return CryptoCurrency.ada;
|
||||
case 'bch':
|
||||
return CryptoCurrency.bch;
|
||||
case 'bnb':
|
||||
return CryptoCurrency.bnb;
|
||||
case 'btc':
|
||||
return CryptoCurrency.btc;
|
||||
case 'dash':
|
||||
return CryptoCurrency.dash;
|
||||
case 'eos':
|
||||
return CryptoCurrency.eos;
|
||||
case 'eth':
|
||||
return CryptoCurrency.eth;
|
||||
case 'ltc':
|
||||
return CryptoCurrency.ltc;
|
||||
case 'nano':
|
||||
return CryptoCurrency.nano;
|
||||
case 'trx':
|
||||
return CryptoCurrency.trx;
|
||||
case 'usdt':
|
||||
return CryptoCurrency.usdt;
|
||||
case 'xlm':
|
||||
return CryptoCurrency.xlm;
|
||||
case 'xrp':
|
||||
return CryptoCurrency.xrp;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => title;
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
|
||||
|
||||
String cryptoToString(CryptoCurrency crypto) {
|
||||
switch (crypto) {
|
||||
case CryptoCurrency.xmr:
|
||||
return 'XMR';
|
||||
case CryptoCurrency.btc:
|
||||
return 'BTC';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
@@ -1,178 +0,0 @@
|
||||
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:cake_wallet/src/domain/common/node.dart';
|
||||
import 'package:cake_wallet/src/domain/common/balance_display_mode.dart';
|
||||
import 'package:cake_wallet/src/domain/common/fiat_currency.dart';
|
||||
import 'package:cake_wallet/src/domain/common/node_list.dart';
|
||||
import 'package:cake_wallet/src/domain/common/transaction_priority.dart';
|
||||
|
||||
Future defaultSettingsMigration(
|
||||
{@required int version,
|
||||
@required SharedPreferences sharedPreferences,
|
||||
@required Box<Node> nodes}) async {
|
||||
final currentVersion =
|
||||
sharedPreferences.getInt('current_default_settings_migration_version') ??
|
||||
0;
|
||||
if (currentVersion >= version) {
|
||||
return;
|
||||
}
|
||||
|
||||
final migrationVersionsLength = version - currentVersion;
|
||||
final migrationVersions = List<int>.generate(
|
||||
migrationVersionsLength, (i) => currentVersion + (i + 1));
|
||||
|
||||
await Future.forEach(migrationVersions, (int version) async {
|
||||
try {
|
||||
switch (version) {
|
||||
case 1:
|
||||
await sharedPreferences.setString(
|
||||
SettingsStoreBase.currentFiatCurrencyKey,
|
||||
FiatCurrency.usd.toString());
|
||||
await sharedPreferences.setInt(
|
||||
SettingsStoreBase.currentTransactionPriorityKey,
|
||||
TransactionPriority.standart.raw);
|
||||
await sharedPreferences.setInt(
|
||||
SettingsStoreBase.currentBalanceDisplayModeKey,
|
||||
BalanceDisplayMode.availableBalance.raw);
|
||||
await sharedPreferences.setBool('save_recipient_address', true);
|
||||
await resetToDefault(nodes);
|
||||
await changeMoneroCurrentNodeToDefault(
|
||||
sharedPreferences: sharedPreferences, nodes: nodes);
|
||||
await changeBitcoinCurrentElectrumServerToDefault(
|
||||
sharedPreferences: sharedPreferences, nodes: nodes);
|
||||
|
||||
break;
|
||||
case 2:
|
||||
await replaceNodesMigration(nodes: nodes);
|
||||
await replaceDefaultNode(
|
||||
sharedPreferences: sharedPreferences, nodes: nodes);
|
||||
|
||||
break;
|
||||
case 3:
|
||||
await updateNodeTypes(nodes: nodes);
|
||||
await addBitcoinElectrumServerList(nodes: nodes);
|
||||
|
||||
break;
|
||||
case 4:
|
||||
await changeBitcoinCurrentElectrumServerToDefault(
|
||||
sharedPreferences: sharedPreferences, nodes: nodes);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
await sharedPreferences.setInt(
|
||||
'current_default_settings_migration_version', version);
|
||||
} catch (e) {
|
||||
print('Migration error: ${e.toString()}');
|
||||
}
|
||||
});
|
||||
|
||||
await sharedPreferences.setInt(
|
||||
'current_default_settings_migration_version', version);
|
||||
}
|
||||
|
||||
Future<void> replaceNodesMigration({@required Box<Node> nodes}) async {
|
||||
final replaceNodes = <String, Node>{
|
||||
'eu-node.cakewallet.io:18081':
|
||||
Node(uri: 'xmr-node-eu.cakewallet.com:18081', type: WalletType.monero),
|
||||
'node.cakewallet.io:18081': Node(
|
||||
uri: 'xmr-node-usa-east.cakewallet.com:18081', type: WalletType.monero),
|
||||
'node.xmr.ru:13666':
|
||||
Node(uri: 'node.monero.net:18081', type: WalletType.monero)
|
||||
};
|
||||
|
||||
nodes.values.forEach((Node node) async {
|
||||
final nodeToReplace = replaceNodes[node.uri];
|
||||
|
||||
if (nodeToReplace != null) {
|
||||
node.uri = nodeToReplace.uri;
|
||||
node.login = nodeToReplace.login;
|
||||
node.password = nodeToReplace.password;
|
||||
await node.save();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> changeMoneroCurrentNodeToDefault(
|
||||
{@required SharedPreferences sharedPreferences,
|
||||
@required Box<Node> nodes}) async {
|
||||
final node = getMoneroDefaultNode(nodes: nodes);
|
||||
final nodeId = node?.key as int ?? 0; // 0 - England
|
||||
|
||||
await sharedPreferences.setInt('current_node_id', nodeId);
|
||||
}
|
||||
|
||||
Node getBitcoinDefaultElectrumServer({@required Box<Node> nodes}) {
|
||||
final uri = 'electrumx.cakewallet.com:50002';
|
||||
|
||||
return nodes.values
|
||||
.firstWhere((Node node) => node.uri == uri, orElse: () => null) ??
|
||||
nodes.values.firstWhere((node) => node.type == WalletType.bitcoin,
|
||||
orElse: () => null);
|
||||
}
|
||||
|
||||
Node getMoneroDefaultNode({@required Box<Node> nodes}) {
|
||||
final timeZone = DateTime.now().timeZoneOffset.inHours;
|
||||
var nodeUri = '';
|
||||
|
||||
if (timeZone >= 1) {
|
||||
// Eurasia
|
||||
nodeUri = 'xmr-node-eu.cakewallet.com:18081';
|
||||
} else if (timeZone <= -4) {
|
||||
// America
|
||||
nodeUri = 'xmr-node-usa-east.cakewallet.com:18081';
|
||||
}
|
||||
|
||||
return nodes.values
|
||||
.firstWhere((Node node) => node.uri == nodeUri, orElse: () => null) ??
|
||||
nodes.values.first;
|
||||
}
|
||||
|
||||
Future<void> changeBitcoinCurrentElectrumServerToDefault(
|
||||
{@required SharedPreferences sharedPreferences,
|
||||
@required Box<Node> nodes}) async {
|
||||
final server = getBitcoinDefaultElectrumServer(nodes: nodes);
|
||||
final serverId = server?.key as int ?? 0;
|
||||
|
||||
await sharedPreferences.setInt('current_node_id_btc', serverId);
|
||||
}
|
||||
|
||||
Future<void> replaceDefaultNode(
|
||||
{@required SharedPreferences sharedPreferences,
|
||||
@required Box<Node> nodes}) async {
|
||||
const nodesForReplace = <String>[
|
||||
'xmr-node-uk.cakewallet.com:18081',
|
||||
'eu-node.cakewallet.io:18081',
|
||||
'node.cakewallet.io:18081'
|
||||
];
|
||||
final currentNodeId = sharedPreferences.getInt('current_node_id');
|
||||
final currentNode =
|
||||
nodes.values.firstWhere((Node node) => node.key == currentNodeId);
|
||||
final needToReplace =
|
||||
currentNode == null ? true : nodesForReplace.contains(currentNode.uri);
|
||||
|
||||
if (!needToReplace) {
|
||||
return;
|
||||
}
|
||||
|
||||
await changeMoneroCurrentNodeToDefault(
|
||||
sharedPreferences: sharedPreferences, nodes: nodes);
|
||||
}
|
||||
|
||||
Future<void> updateNodeTypes({@required Box<Node> nodes}) async {
|
||||
nodes.values.forEach((node) async {
|
||||
if (node.type == null) {
|
||||
node.type = WalletType.monero;
|
||||
await node.save();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> addBitcoinElectrumServerList({@required Box<Node> nodes}) async {
|
||||
final serverList = await loadElectrumServerList();
|
||||
await nodes.addAll(serverList);
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
import 'dart:convert';
|
||||
import 'package:dio/dio.dart' as __dio;
|
||||
import 'package:crypto/crypto.dart' as crypto;
|
||||
import 'dart:math' as math;
|
||||
|
||||
class DigestRequest {
|
||||
final md5 = crypto.md5;
|
||||
|
||||
String generateCnonce() {
|
||||
final rnd = math.Random.secure();
|
||||
final values = List<int>.generate(32, (i) => rnd.nextInt(256));
|
||||
return base64Url.encode(values).substring(0, 8);
|
||||
}
|
||||
|
||||
String generateHA1({String realm, String username, String password}) {
|
||||
final ha1CredentialsData =
|
||||
Utf8Encoder().convert('$username:$realm:$password');
|
||||
final ha1 = md5.convert(ha1CredentialsData).toString();
|
||||
|
||||
return ha1;
|
||||
}
|
||||
|
||||
String generateHA2({String method, String uri}) {
|
||||
final ha2Data = Utf8Encoder().convert('$method:$uri');
|
||||
final ha2 = md5.convert(ha2Data).toString();
|
||||
|
||||
return ha2;
|
||||
}
|
||||
|
||||
String generateResponseString(
|
||||
{String ha1,
|
||||
String ha2,
|
||||
String nonce,
|
||||
String nonceCount,
|
||||
String cnonce,
|
||||
String qop}) {
|
||||
final responseData =
|
||||
Utf8Encoder().convert('$ha1:$nonce:$nonceCount:$cnonce:$qop:$ha2');
|
||||
final response = md5.convert(responseData).toString();
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
Map<String, String> parsetAuthorizationHeader({String source}) {
|
||||
final authHeaderParts =
|
||||
source.substring(7).split(',').map((item) => item.trim());
|
||||
final authenticate = Map<String, String>();
|
||||
|
||||
for (final part in authHeaderParts) {
|
||||
final kv = part.split('=');
|
||||
authenticate[kv[0]] =
|
||||
kv.getRange(1, kv.length).join('=').replaceAll('"', '');
|
||||
}
|
||||
|
||||
return authenticate;
|
||||
}
|
||||
|
||||
Future<__dio.Response> request(
|
||||
{String uri, String login, String password}) async {
|
||||
const path = '/json_rpc';
|
||||
const method = 'POST';
|
||||
final url = Uri.http(uri, path);
|
||||
final dio = __dio.Dio();
|
||||
final headers = {'Content-type': 'application/json'};
|
||||
final body =
|
||||
json.encode({"jsonrpc": "2.0", "id": "0", "method": "get_info"});
|
||||
final credentialsResponse = await dio.post<Object>(url.toString(),
|
||||
options: __dio.Options(headers: headers, validateStatus: (_) => true));
|
||||
final authenticate = parsetAuthorizationHeader(
|
||||
source: credentialsResponse.headers['www-authenticate'].first);
|
||||
final qop = authenticate['qop'];
|
||||
final algorithm = 'MD5';
|
||||
final realm = 'monero-rpc';
|
||||
final nonce = authenticate['nonce'];
|
||||
final cnonce = generateCnonce();
|
||||
final nonceCount = '00000001';
|
||||
final ha1 = generateHA1(realm: realm, username: login, password: password);
|
||||
final ha2 = generateHA2(method: method, uri: path);
|
||||
final response = generateResponseString(
|
||||
ha1: ha1,
|
||||
ha2: ha2,
|
||||
nonce: nonce,
|
||||
nonceCount: nonceCount,
|
||||
cnonce: cnonce,
|
||||
qop: qop);
|
||||
|
||||
final authorizationHeaders = {
|
||||
'Content-type': 'application/json',
|
||||
'Authorization':
|
||||
'Digest username="$login",realm="$realm",nonce="$nonce",uri="$path",algorithm="$algorithm",qop=$qop,nc=$nonceCount,cnonce="$cnonce",response="$response"'
|
||||
};
|
||||
|
||||
return await dio.post<Object>(url.toString(),
|
||||
options: __dio.Options(headers: authorizationHeaders), data: body);
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
import 'package:encrypt/encrypt.dart';
|
||||
import 'package:password/password.dart';
|
||||
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
||||
|
||||
String encrypt({String source, String key, int keyLength = 16}) {
|
||||
final _key = Key.fromUtf8(key);
|
||||
final iv = IV.fromLength(keyLength);
|
||||
final encrypter = Encrypter(AES(_key));
|
||||
final encrypted = encrypter.encrypt(source, iv: iv);
|
||||
|
||||
return encrypted.base64;
|
||||
}
|
||||
|
||||
String decrypt({String source, String key, int keyLength = 16}) {
|
||||
final _key = Key.fromUtf8(key);
|
||||
final iv = IV.fromLength(keyLength);
|
||||
final encrypter = Encrypter(AES(_key));
|
||||
final decrypted = encrypter.decrypt64(source, iv: iv);
|
||||
|
||||
return decrypted;
|
||||
}
|
||||
|
||||
String hash({String source}) {
|
||||
final algorithm = PBKDF2();
|
||||
final hash = Password.hash(source, algorithm);
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
String encodedPinCode({String pin}) {
|
||||
final source = '${secrets.salt}$pin';
|
||||
|
||||
return encrypt(source: source, key: secrets.key);
|
||||
}
|
||||
|
||||
String decodedPinCode({String pin}) {
|
||||
final decrypted = decrypt(source: pin, key: secrets.key);
|
||||
|
||||
return decrypted.substring(secrets.key.length, decrypted.length);
|
||||
}
|
||||
|
||||
String encodeWalletPassword({String password}) {
|
||||
final source = password;
|
||||
final _key = secrets.shortKey + secrets.walletSalt;
|
||||
|
||||
return encrypt(source: source, key: _key);
|
||||
}
|
||||
|
||||
String decodeWalletPassword({String password}) {
|
||||
final source = password;
|
||||
final _key = secrets.shortKey + secrets.walletSalt;
|
||||
|
||||
return decrypt(source: source, key: _key);
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
abstract class EnumerableItem<T> {
|
||||
const EnumerableItem({@required this.title, @required this.raw});
|
||||
|
||||
final T raw;
|
||||
final String title;
|
||||
|
||||
@override
|
||||
String toString() => title;
|
||||
}
|
||||
|
||||
mixin Serializable<T> on EnumerableItem<T> {
|
||||
static Serializable deserialize<T>({T raw}) => null;
|
||||
T serialize() => raw;
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
import 'dart:convert';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
|
||||
import 'package:cake_wallet/src/domain/common/fiat_currency.dart';
|
||||
import 'package:cake_wallet/src/domain/common/currency_formatter.dart';
|
||||
|
||||
const fiatApiAuthority = 'fiat-api.cakewallet.com';
|
||||
const fiatApiPath = '/v1/rates';
|
||||
|
||||
Future<double> fetchPriceFor({CryptoCurrency crypto, FiatCurrency fiat}) async {
|
||||
double price = 0.0;
|
||||
|
||||
try {
|
||||
final fiatStringified = fiat.toString();
|
||||
final uri =
|
||||
Uri.https(fiatApiAuthority, fiatApiPath, {'convert': fiatStringified});
|
||||
final response = await get(uri.toString());
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final data = responseJSON['data'] as List<dynamic>;
|
||||
|
||||
for (final item in data) {
|
||||
if (item['symbol'] == cryptoToString(crypto)) {
|
||||
price = item['quote'][fiatStringified]['price'] as double;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return price;
|
||||
} catch (e) {
|
||||
return price;
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
import 'package:cake_wallet/src/domain/common/enumerable_item.dart';
|
||||
|
||||
class FiatCurrency extends EnumerableItem<String> with Serializable<String> {
|
||||
const FiatCurrency({String symbol}) : super(title: symbol, raw: symbol);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => other is FiatCurrency && other.raw == raw;
|
||||
|
||||
static const all = [
|
||||
FiatCurrency.aud,
|
||||
FiatCurrency.bgn,
|
||||
FiatCurrency.brl,
|
||||
FiatCurrency.cad,
|
||||
FiatCurrency.chf,
|
||||
FiatCurrency.cny,
|
||||
FiatCurrency.czk,
|
||||
FiatCurrency.eur,
|
||||
FiatCurrency.dkk,
|
||||
FiatCurrency.gbp,
|
||||
FiatCurrency.hkd,
|
||||
FiatCurrency.hrk,
|
||||
FiatCurrency.huf,
|
||||
FiatCurrency.idr,
|
||||
FiatCurrency.ils,
|
||||
FiatCurrency.inr,
|
||||
FiatCurrency.isk,
|
||||
FiatCurrency.jpy,
|
||||
FiatCurrency.krw,
|
||||
FiatCurrency.mxn,
|
||||
FiatCurrency.myr,
|
||||
FiatCurrency.nok,
|
||||
FiatCurrency.nzd,
|
||||
FiatCurrency.php,
|
||||
FiatCurrency.pln,
|
||||
FiatCurrency.ron,
|
||||
FiatCurrency.rub,
|
||||
FiatCurrency.sek,
|
||||
FiatCurrency.sgd,
|
||||
FiatCurrency.thb,
|
||||
FiatCurrency.usd,
|
||||
FiatCurrency.zar,
|
||||
FiatCurrency.vef
|
||||
];
|
||||
|
||||
static const aud = FiatCurrency(symbol: 'AUD');
|
||||
static const bgn = FiatCurrency(symbol: 'BGN');
|
||||
static const brl = FiatCurrency(symbol: 'BRL');
|
||||
static const cad = FiatCurrency(symbol: 'CAD');
|
||||
static const chf = FiatCurrency(symbol: 'CHF');
|
||||
static const cny = FiatCurrency(symbol: 'CNY');
|
||||
static const czk = FiatCurrency(symbol: 'CZK');
|
||||
static const eur = FiatCurrency(symbol: 'EUR');
|
||||
static const dkk = FiatCurrency(symbol: 'DKK');
|
||||
static const gbp = FiatCurrency(symbol: 'GBP');
|
||||
static const hkd = FiatCurrency(symbol: 'HKD');
|
||||
static const hrk = FiatCurrency(symbol: 'HRK');
|
||||
static const huf = FiatCurrency(symbol: 'HUF');
|
||||
static const idr = FiatCurrency(symbol: 'IDR');
|
||||
static const ils = FiatCurrency(symbol: 'ILS');
|
||||
static const inr = FiatCurrency(symbol: 'INR');
|
||||
static const isk = FiatCurrency(symbol: 'ISK');
|
||||
static const jpy = FiatCurrency(symbol: 'JPY');
|
||||
static const krw = FiatCurrency(symbol: 'KRW');
|
||||
static const mxn = FiatCurrency(symbol: 'MXN');
|
||||
static const myr = FiatCurrency(symbol: 'MYR');
|
||||
static const nok = FiatCurrency(symbol: 'NOK');
|
||||
static const nzd = FiatCurrency(symbol: 'NZD');
|
||||
static const php = FiatCurrency(symbol: 'PHP');
|
||||
static const pln = FiatCurrency(symbol: 'PLN');
|
||||
static const ron = FiatCurrency(symbol: 'RON');
|
||||
static const rub = FiatCurrency(symbol: 'RUB');
|
||||
static const sek = FiatCurrency(symbol: 'SEK');
|
||||
static const sgd = FiatCurrency(symbol: 'SGD');
|
||||
static const thb = FiatCurrency(symbol: 'THB');
|
||||
static const usd = FiatCurrency(symbol: 'USD');
|
||||
static const zar = FiatCurrency(symbol: 'ZAR');
|
||||
static const vef = FiatCurrency(symbol: 'VEF');
|
||||
|
||||
@override
|
||||
int get hashCode => raw.hashCode ^ title.hashCode;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
String formatAmount(String amount) {
|
||||
if (!amount.contains('.')) {
|
||||
return amount + '.00';
|
||||
} else if (amount.endsWith('.')) {
|
||||
return amount + '00';
|
||||
}
|
||||
return amount;
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
import 'dart:io';
|
||||
import 'dart:convert';
|
||||
import 'package:cake_wallet/src/domain/common/contact.dart';
|
||||
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
|
||||
import 'package:cake_wallet/src/domain/common/wallet_info.dart';
|
||||
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/trade.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
const reservedNames = ["flutter_assets", "wallets", "db"];
|
||||
|
||||
Future<void> migrate_android_v1() async {
|
||||
final appDocDir = await getApplicationDocumentsDirectory();
|
||||
|
||||
await migrate_hives(appDocDir: appDocDir);
|
||||
await migrate_wallets(appDocDir: appDocDir);
|
||||
}
|
||||
|
||||
Future<void> migrate_ios_v1() async {
|
||||
final appDocDir = await getApplicationDocumentsDirectory();
|
||||
}
|
||||
|
||||
Future<void> migrate_hives({Directory appDocDir}) async {
|
||||
final dbDir = Directory('${appDocDir.path}/db');
|
||||
final files = List<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> migrate_wallets({Directory appDocDir}) async {
|
||||
final walletsDir = Directory('${appDocDir.path}/wallets');
|
||||
final moneroWalletsDir = Directory('${walletsDir.path}/monero');
|
||||
final dirs = List<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> migrate_ios_wallet_info(
|
||||
{@required Directory appDocDir,
|
||||
@required Box<WalletInfo> walletsInfo}) async {
|
||||
// final walletsDir = Directory('${appDocDir.path}/wallets');
|
||||
// final moneroWalletsDir = Directory('${walletsDir.path}/monero');
|
||||
|
||||
// moneroWalletsDir.listSync().forEach((item) async {
|
||||
// try {
|
||||
// if (item is Directory) {
|
||||
// final name = item.path.split('/').last;
|
||||
// final configFile = File('${item.path}/$name.json');
|
||||
// final config =
|
||||
// json.decode(configFile.readAsStringSync()) as Map<String, dynamic>;
|
||||
// final isRecovery = config["isRecovery"] as bool ?? false;
|
||||
// final id =
|
||||
// walletTypeToString(WalletType.monero).toLowerCase() + '_' + name;
|
||||
// final walletInfo =
|
||||
// WalletInfo(id: id, name: name, isRecovery: isRecovery);
|
||||
|
||||
// await walletsInfo.add(walletInfo);
|
||||
// }
|
||||
// } catch (e) {
|
||||
// print(e.toString());
|
||||
// }
|
||||
// });
|
||||
}
|
||||
|
||||
Future<void> migrate_ios_trades_list(
|
||||
{@required Directory appDocDir, @required Box<Trade> trades}) async {
|
||||
final adderessBookJSON = File('${appDocDir.path}/trades_list.json');
|
||||
final List<dynamic> trades =
|
||||
json.decode(adderessBookJSON.readAsStringSync()) as List<dynamic>;
|
||||
}
|
||||
|
||||
Future<void> migrate_ios_address_book(
|
||||
{@required Directory appDocDir, @required Box<Contact> contacts}) async {
|
||||
final adderessBookJSON = File('${appDocDir.path}/address_book.json');
|
||||
final List<dynamic> addresses =
|
||||
json.decode(adderessBookJSON.readAsStringSync()) as List<dynamic>;
|
||||
|
||||
addresses.forEach((dynamic item) async {
|
||||
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;
|
||||
final contact = Contact(
|
||||
address: address, name: name, type: CryptoCurrency.fromString(type));
|
||||
|
||||
await contacts.add(contact);
|
||||
});
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
Future<List<int>> getEncryptionKey(
|
||||
{String forKey, FlutterSecureStorage secureStorage}) async {
|
||||
final stringifiedKey =
|
||||
await secureStorage.read(key: 'transactionDescriptionsBoxKey');
|
||||
List<int> key;
|
||||
|
||||
if (stringifiedKey == null) {
|
||||
key = Hive.generateSecureKey();
|
||||
final keyStringified = key.join(',');
|
||||
await secureStorage.write(
|
||||
key: 'transactionDescriptionsBoxKey', value: keyStringified);
|
||||
} else {
|
||||
key = stringifiedKey
|
||||
.split(',')
|
||||
.map((i) => int.parse(i))
|
||||
.toList();
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:devicelocale/devicelocale.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
const Map<String, String> languages = {
|
||||
'en': 'English',
|
||||
'de': 'Deutsch (German)',
|
||||
'es': 'Español (Spanish)',
|
||||
'hi': 'हिंदी (Hindi)',
|
||||
'ja': '日本 (Japanese)',
|
||||
'ko': '한국어 (Korean)',
|
||||
'nl': 'Nederlands (Dutch)',
|
||||
'pl': 'Polski (Polish)',
|
||||
'pt': 'Português (Portuguese)',
|
||||
'ru': 'Русский (Russian)',
|
||||
'uk': 'Українська (Ukrainian)',
|
||||
'zh': '中文 (Chinese)'
|
||||
};
|
||||
|
||||
class Language with ChangeNotifier {
|
||||
Language(this._currentLanguage);
|
||||
|
||||
String _currentLanguage;
|
||||
|
||||
String getCurrentLanguage() => _currentLanguage;
|
||||
|
||||
void setCurrentLanguage(String language) {
|
||||
_currentLanguage = language;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
static Future<String> localeDetection() async {
|
||||
var locale = await Devicelocale.currentLocale;
|
||||
locale = Intl.shortLocale(locale);
|
||||
|
||||
return languages.keys.contains(locale) ? locale : 'en';
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
class MnemonicItem {
|
||||
MnemonicItem({String text}) : _text = text;
|
||||
|
||||
String get text => _text;
|
||||
String _text;
|
||||
|
||||
void changeText(String text) => _text = text;
|
||||
|
||||
@override
|
||||
String toString() => text;
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
import 'package:cake_wallet/utils/mobx.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'dart:convert';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
|
||||
import 'package:cake_wallet/src/domain/common/digest_request.dart';
|
||||
|
||||
part 'node.g.dart';
|
||||
|
||||
@HiveType(typeId: 1)
|
||||
class Node extends HiveObject with Keyable {
|
||||
Node(
|
||||
{@required this.uri,
|
||||
@required WalletType type,
|
||||
this.login,
|
||||
this.password}) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
Node.fromMap(Map map)
|
||||
: uri = map['uri'] as String ?? '',
|
||||
login = map['login'] as String,
|
||||
password = map['password'] as String,
|
||||
typeRaw = map['typeRaw'] as int;
|
||||
|
||||
static const boxName = 'Nodes';
|
||||
|
||||
@HiveField(0)
|
||||
String uri;
|
||||
|
||||
@HiveField(1)
|
||||
String login;
|
||||
|
||||
@HiveField(2)
|
||||
String password;
|
||||
|
||||
@HiveField(3)
|
||||
int typeRaw;
|
||||
|
||||
WalletType get type => deserializeFromInt(typeRaw);
|
||||
|
||||
set type(WalletType type) => typeRaw = serializeToInt(type);
|
||||
|
||||
Future<bool> requestNode() async {
|
||||
try {
|
||||
switch (type) {
|
||||
case WalletType.monero:
|
||||
return requestMoneroNode();
|
||||
case WalletType.bitcoin:
|
||||
return requestBitcoinElectrumServer();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> requestMoneroNode() async {
|
||||
Map<String, dynamic> resBody;
|
||||
|
||||
if (login != null && password != null) {
|
||||
final digestRequest = DigestRequest();
|
||||
final response = await digestRequest.request(
|
||||
uri: uri, login: login, password: password);
|
||||
resBody = response.data as Map<String, dynamic>;
|
||||
} else {
|
||||
final url = Uri.http(uri, '/json_rpc');
|
||||
final headers = {'Content-type': 'application/json'};
|
||||
final body =
|
||||
json.encode({'jsonrpc': '2.0', 'id': '0', 'method': 'get_info'});
|
||||
final response =
|
||||
await http.post(url.toString(), headers: headers, body: body);
|
||||
resBody = json.decode(response.body) as Map<String, dynamic>;
|
||||
}
|
||||
|
||||
return !(resBody['result']['offline'] as bool);
|
||||
}
|
||||
|
||||
Future<bool> requestBitcoinElectrumServer() async {
|
||||
// FIXME: IMPLEMENT ME
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import "package:yaml/yaml.dart";
|
||||
import 'package:cake_wallet/src/domain/common/node.dart';
|
||||
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
|
||||
|
||||
Future<List<Node>> loadDefaultNodes() async {
|
||||
final nodesRaw = await rootBundle.loadString('assets/node_list.yml');
|
||||
final nodes = loadYaml(nodesRaw) as YamlList;
|
||||
|
||||
return nodes.map((dynamic raw) {
|
||||
if (raw is Map) {
|
||||
final node = Node.fromMap(raw);
|
||||
node?.type = WalletType.monero;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
return null;
|
||||
}).toList();
|
||||
}
|
||||
|
||||
Future<List<Node>> loadElectrumServerList() async {
|
||||
final serverListRaw =
|
||||
await rootBundle.loadString('assets/electrum_server_list.yml');
|
||||
final serverList = loadYaml(serverListRaw) as YamlList;
|
||||
|
||||
return serverList.map((dynamic raw) {
|
||||
if (raw is Map) {
|
||||
final node = Node.fromMap(raw);
|
||||
node?.type = WalletType.bitcoin;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
return null;
|
||||
}).toList();
|
||||
}
|
||||
|
||||
Future resetToDefault(Box<Node> nodeSource) async {
|
||||
final moneroNodes = await loadDefaultNodes();
|
||||
final bitcoinElectrumServerList = await loadElectrumServerList();
|
||||
final nodes = moneroNodes + bitcoinElectrumServerList;
|
||||
|
||||
await nodeSource.clear();
|
||||
await nodeSource.addAll(nodes);
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
import 'package:basic_utils/basic_utils.dart';
|
||||
|
||||
class OpenaliasRecord {
|
||||
|
||||
OpenaliasRecord({this.address, this.name});
|
||||
|
||||
final String name;
|
||||
final String address;
|
||||
|
||||
static String formatDomainName(String name) {
|
||||
String formattedName = name;
|
||||
|
||||
if (name.contains("@")) {
|
||||
formattedName = name.replaceAll("@", ".");
|
||||
}
|
||||
|
||||
return formattedName;
|
||||
}
|
||||
|
||||
static Future<OpenaliasRecord> fetchAddressAndName(String formattedName) async {
|
||||
String address = formattedName;
|
||||
String name = formattedName;
|
||||
|
||||
if (formattedName.contains(".")) {
|
||||
try {
|
||||
final txtRecord = await DnsUtils.lookupRecord(formattedName, RRecordType.TXT, dnssec: true);
|
||||
|
||||
if (txtRecord != null) {
|
||||
|
||||
for (RRecord element in txtRecord) {
|
||||
String record = element.data;
|
||||
|
||||
if (record.contains("oa1:xmr") && record.contains("recipient_address")) {
|
||||
record = record.replaceAll('\"', "");
|
||||
|
||||
final dataList = record.split(";");
|
||||
|
||||
address = dataList.where((item) => (item.contains("recipient_address")))
|
||||
.toString().replaceAll("oa1:xmr recipient_address=", "")
|
||||
.replaceAll("(", "").replaceAll(")", "").trim();
|
||||
|
||||
final recipientName = dataList.where((item) => (item.contains("recipient_name"))).toString()
|
||||
.replaceAll("(", "").replaceAll(")", "").trim();
|
||||
|
||||
if (recipientName.isNotEmpty) {
|
||||
name = recipientName.replaceAll("recipient_name=", "");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
print("${e.toString()}");
|
||||
}
|
||||
}
|
||||
|
||||
return OpenaliasRecord(address: address, name: name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
bool parseBoolFromString(String string) {
|
||||
return string.toString() == 'true';
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import 'dart:io';
|
||||
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
Future<String> pathForWalletDir({@required String name, @required WalletType type}) async {
|
||||
final root = await getApplicationDocumentsDirectory();
|
||||
final prefix = walletTypeToString(type).toLowerCase();
|
||||
final walletsDir = Directory('${root.path}/wallets');
|
||||
final walletDire = Directory('${walletsDir.path}/$prefix/$name');
|
||||
|
||||
if (!walletDire.existsSync()) {
|
||||
walletDire.createSync(recursive: true);
|
||||
}
|
||||
|
||||
return walletDire.path;
|
||||
}
|
||||
|
||||
Future<String> pathForWallet({@required String name, @required WalletType type}) async =>
|
||||
await pathForWalletDir(name: name, type: type)
|
||||
.then((path) => path + '/$name');
|
||||
@@ -1,25 +0,0 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cw_monero/transaction_history.dart' as transaction_history;
|
||||
import 'package:cw_monero/structs/pending_transaction.dart';
|
||||
import 'package:cake_wallet/src/domain/monero/monero_amount_format.dart';
|
||||
|
||||
class PendingTransaction {
|
||||
PendingTransaction(
|
||||
{@required this.amount, @required this.fee, @required this.hash});
|
||||
|
||||
PendingTransaction.fromTransactionDescription(
|
||||
PendingTransactionDescription transactionDescription)
|
||||
: amount = moneroAmountToString(amount: transactionDescription.amount),
|
||||
fee = moneroAmountToString(amount: transactionDescription.fee),
|
||||
hash = transactionDescription.hash,
|
||||
_pointerAddress = transactionDescription.pointerAddress;
|
||||
|
||||
final String amount;
|
||||
final String fee;
|
||||
final String hash;
|
||||
|
||||
int _pointerAddress;
|
||||
|
||||
Future<void> commit() async => transaction_history
|
||||
.commitTransactionFromPointerAddress(address: _pointerAddress);
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import 'package:barcode_scan/barcode_scan.dart';
|
||||
|
||||
var isQrScannerShown = false;
|
||||
|
||||
Future<String> presentQRScanner() async {
|
||||
isQrScannerShown = true;
|
||||
try {
|
||||
final result = await BarcodeScanner.scan();
|
||||
isQrScannerShown = false;
|
||||
return result.rawContent;
|
||||
} catch (e) {
|
||||
isQrScannerShown = false;
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
enum SecretStoreKey { moneroWalletPassword, pinCodePassword }
|
||||
|
||||
const moneroWalletPassword = "MONERO_WALLET_PASSWORD";
|
||||
const pinCodePassword = "PIN_CODE_PASSWORD";
|
||||
|
||||
String generateStoreKeyFor({SecretStoreKey key, String walletName = "",}) {
|
||||
var _key = "";
|
||||
|
||||
switch (key) {
|
||||
case SecretStoreKey.moneroWalletPassword:
|
||||
{
|
||||
_key = moneroWalletPassword + "_" + walletName.toUpperCase();
|
||||
}
|
||||
break;
|
||||
|
||||
case SecretStoreKey.pinCodePassword:
|
||||
{
|
||||
_key = pinCodePassword;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
{}
|
||||
}
|
||||
|
||||
return _key;
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
|
||||
abstract class SyncStatus {
|
||||
const SyncStatus();
|
||||
|
||||
double progress();
|
||||
|
||||
String title();
|
||||
}
|
||||
|
||||
class SyncingSyncStatus extends SyncStatus {
|
||||
SyncingSyncStatus(this.blocksLeft, this.ptc);
|
||||
|
||||
final double ptc;
|
||||
final int blocksLeft;
|
||||
|
||||
@override
|
||||
double progress() => ptc;
|
||||
|
||||
@override
|
||||
String title() => S.current.sync_status_syncronizing;
|
||||
|
||||
@override
|
||||
String toString() => '$blocksLeft';
|
||||
}
|
||||
|
||||
class SyncedSyncStatus extends SyncStatus {
|
||||
@override
|
||||
double progress() => 1.0;
|
||||
|
||||
@override
|
||||
String title() => S.current.sync_status_syncronized;
|
||||
}
|
||||
|
||||
class NotConnectedSyncStatus extends SyncStatus {
|
||||
const NotConnectedSyncStatus();
|
||||
|
||||
@override
|
||||
double progress() => 0.0;
|
||||
|
||||
@override
|
||||
String title() => S.current.sync_status_not_connected;
|
||||
}
|
||||
|
||||
class StartingSyncStatus extends SyncStatus {
|
||||
@override
|
||||
double progress() => 0.0;
|
||||
|
||||
@override
|
||||
String title() => S.current.sync_status_starting_sync;
|
||||
}
|
||||
|
||||
class FailedSyncStatus extends SyncStatus {
|
||||
@override
|
||||
double progress() => 1.0;
|
||||
|
||||
@override
|
||||
String title() => S.current.sync_status_failed_connect;
|
||||
}
|
||||
|
||||
class ConnectingSyncStatus extends SyncStatus {
|
||||
@override
|
||||
double progress() => 0.0;
|
||||
|
||||
@override
|
||||
String title() => S.current.sync_status_connecting;
|
||||
}
|
||||
|
||||
class ConnectedSyncStatus extends SyncStatus {
|
||||
@override
|
||||
double progress() => 0.0;
|
||||
|
||||
@override
|
||||
String title() => S.current.sync_status_connected;
|
||||
}
|
||||
|
||||
class LostConnectionSyncStatus extends SyncStatus {
|
||||
@override
|
||||
double progress() => 1.0;
|
||||
|
||||
@override
|
||||
String title() => S.current.sync_status_failed_connect;
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
part 'template.g.dart';
|
||||
|
||||
@HiveType(typeId: 6)
|
||||
class Template extends HiveObject {
|
||||
Template({this.name, this.address, this.cryptoCurrency, this.amount});
|
||||
|
||||
static const boxName = 'Template';
|
||||
|
||||
@HiveField(0)
|
||||
String name;
|
||||
|
||||
@HiveField(1)
|
||||
String address;
|
||||
|
||||
@HiveField(2)
|
||||
String cryptoCurrency;
|
||||
|
||||
@HiveField(3)
|
||||
String amount;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
abstract class TransactionCreationCredentials {}
|
||||
@@ -1,17 +0,0 @@
|
||||
enum TransactionDirection { incoming, outgoing }
|
||||
|
||||
TransactionDirection parseTransactionDirectionFromInt(int raw) {
|
||||
switch (raw) {
|
||||
case 0: return TransactionDirection.incoming;
|
||||
case 1: return TransactionDirection.outgoing;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
TransactionDirection parseTransactionDirectionFromNumber(String raw) {
|
||||
switch (raw) {
|
||||
case "0": return TransactionDirection.incoming;
|
||||
case "1": return TransactionDirection.outgoing;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import 'package:rxdart/rxdart.dart';
|
||||
import 'package:cake_wallet/src/domain/common/transaction_info.dart';
|
||||
|
||||
abstract class TransactionHistory {
|
||||
Observable<List<TransactionInfo>> transactions;
|
||||
Future<List<TransactionInfo>> getAll();
|
||||
Future update();
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import 'package:cake_wallet/src/domain/common/transaction_direction.dart';
|
||||
|
||||
abstract class TransactionInfo extends Object {
|
||||
String id;
|
||||
int amount;
|
||||
TransactionDirection direction;
|
||||
bool isPending;
|
||||
DateTime date;
|
||||
int height;
|
||||
int confirmations;
|
||||
String amountFormatted();
|
||||
String fiatAmount();
|
||||
void changeFiatAmount(String amount);
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/domain/common/enumerable_item.dart';
|
||||
|
||||
class TransactionPriority extends EnumerableItem<int> with Serializable<int> {
|
||||
const TransactionPriority({String title, int raw})
|
||||
: super(title: title, raw: raw);
|
||||
|
||||
static const all = [
|
||||
TransactionPriority.slow,
|
||||
TransactionPriority.regular,
|
||||
TransactionPriority.medium,
|
||||
TransactionPriority.fast,
|
||||
TransactionPriority.fastest
|
||||
];
|
||||
static const slow = TransactionPriority(title: 'Slow', raw: 0);
|
||||
static const regular = TransactionPriority(title: 'Regular', raw: 1);
|
||||
static const medium = TransactionPriority(title: 'Medium', raw: 2);
|
||||
static const fast = TransactionPriority(title: 'Fast', raw: 3);
|
||||
static const fastest = TransactionPriority(title: 'Fastest', raw: 4);
|
||||
static const standart = slow;
|
||||
|
||||
static TransactionPriority deserialize({int raw}) {
|
||||
switch (raw) {
|
||||
case 0:
|
||||
return slow;
|
||||
case 1:
|
||||
return regular;
|
||||
case 2:
|
||||
return medium;
|
||||
case 3:
|
||||
return fast;
|
||||
case 4:
|
||||
return fastest;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
switch (this) {
|
||||
case TransactionPriority.slow:
|
||||
return S.current.transaction_priority_slow;
|
||||
case TransactionPriority.regular:
|
||||
return S.current.transaction_priority_regular;
|
||||
case TransactionPriority.medium:
|
||||
return S.current.transaction_priority_medium;
|
||||
case TransactionPriority.fast:
|
||||
return S.current.transaction_priority_fast;
|
||||
case TransactionPriority.fastest:
|
||||
return S.current.transaction_priority_fastest;
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
import 'package:rxdart/rxdart.dart';
|
||||
import 'package:cake_wallet/src/domain/common/sync_status.dart';
|
||||
import 'package:cake_wallet/src/domain/common/transaction_history.dart';
|
||||
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
|
||||
import 'package:cake_wallet/src/domain/common/transaction_creation_credentials.dart';
|
||||
import 'package:cake_wallet/src/domain/common/pending_transaction.dart';
|
||||
import 'package:cake_wallet/src/domain/common/balance.dart';
|
||||
import 'package:cake_wallet/src/domain/common/node.dart';
|
||||
|
||||
abstract class Wallet {
|
||||
WalletType getType();
|
||||
|
||||
WalletType walletType;
|
||||
|
||||
Observable<Balance> onBalanceChange;
|
||||
|
||||
Observable<SyncStatus> syncStatus;
|
||||
|
||||
Observable<String> get onNameChange;
|
||||
|
||||
Observable<String> get onAddressChange;
|
||||
|
||||
String get name;
|
||||
|
||||
String get address;
|
||||
|
||||
Future updateInfo();
|
||||
|
||||
Future<String> getFilename();
|
||||
|
||||
Future<String> getName();
|
||||
|
||||
Future<String> getAddress();
|
||||
|
||||
Future<String> getSeed();
|
||||
|
||||
Future<Map<String, String>> getKeys();
|
||||
|
||||
Future<String> getFullBalance();
|
||||
|
||||
Future<String> getUnlockedBalance();
|
||||
|
||||
Future<int> getCurrentHeight();
|
||||
|
||||
Future<int> getNodeHeight();
|
||||
|
||||
Future<bool> isConnected();
|
||||
|
||||
Future close();
|
||||
|
||||
TransactionHistory getHistory();
|
||||
|
||||
Future connectToNode({Node node, bool useSSL = false, bool isLightWallet = false});
|
||||
|
||||
Future startSync();
|
||||
|
||||
Future<PendingTransaction> createTransaction(
|
||||
TransactionCreationCredentials credentials);
|
||||
|
||||
Future rescan({int restoreHeight = 0});
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
|
||||
|
||||
class WalletDescription {
|
||||
WalletDescription({this.name, this.type});
|
||||
|
||||
final String name;
|
||||
final WalletType type;
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
|
||||
|
||||
part 'wallet_info.g.dart';
|
||||
|
||||
@HiveType(typeId: 4)
|
||||
class WalletInfo extends HiveObject {
|
||||
WalletInfo(this.id, this.name, this.type, this.isRecovery, this.restoreHeight,
|
||||
this.timestamp, this.dirPath, this.path);
|
||||
|
||||
factory WalletInfo.external(
|
||||
{@required String id,
|
||||
@required String name,
|
||||
@required WalletType type,
|
||||
@required bool isRecovery,
|
||||
@required int restoreHeight,
|
||||
@required DateTime date,
|
||||
@required String dirPath,
|
||||
@required String path}) {
|
||||
return WalletInfo(id, name, type, isRecovery, restoreHeight,
|
||||
date.millisecondsSinceEpoch ?? 0, dirPath, path);
|
||||
}
|
||||
|
||||
static const boxName = 'WalletInfo';
|
||||
|
||||
@HiveField(0)
|
||||
String id;
|
||||
|
||||
@HiveField(1)
|
||||
String name;
|
||||
|
||||
@HiveField(2)
|
||||
WalletType type;
|
||||
|
||||
@HiveField(3)
|
||||
bool isRecovery;
|
||||
|
||||
@HiveField(4)
|
||||
int restoreHeight;
|
||||
|
||||
@HiveField(5)
|
||||
int timestamp;
|
||||
|
||||
@HiveField(6)
|
||||
String dirPath;
|
||||
|
||||
@HiveField(7)
|
||||
String path;
|
||||
|
||||
DateTime get date => DateTime.fromMillisecondsSinceEpoch(timestamp);
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
part 'wallet_type.g.dart';
|
||||
|
||||
const walletTypes = [WalletType.monero, WalletType.bitcoin];
|
||||
|
||||
@HiveType(typeId: 5)
|
||||
enum WalletType {
|
||||
@HiveField(0)
|
||||
monero,
|
||||
|
||||
@HiveField(1)
|
||||
none,
|
||||
|
||||
@HiveField(2)
|
||||
bitcoin
|
||||
}
|
||||
|
||||
int serializeToInt(WalletType type) {
|
||||
switch (type) {
|
||||
case WalletType.monero:
|
||||
return 0;
|
||||
case WalletType.bitcoin:
|
||||
return 1;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
WalletType deserializeFromInt(int raw) {
|
||||
switch (raw) {
|
||||
case 0:
|
||||
return WalletType.monero;
|
||||
case 1:
|
||||
return WalletType.bitcoin;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
String walletTypeToString(WalletType type) {
|
||||
switch (type) {
|
||||
case WalletType.monero:
|
||||
return 'Monero';
|
||||
case WalletType.bitcoin:
|
||||
return 'Bitcoin';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
import 'package:cake_wallet/src/domain/common/wallet.dart';
|
||||
import 'package:cake_wallet/src/domain/common/wallet_description.dart';
|
||||
|
||||
abstract class WalletsManager {
|
||||
Future<Wallet> create(String name, String password, String language);
|
||||
|
||||
Future<Wallet> restoreFromSeed(
|
||||
String name, String password, String seed, int restoreHeight);
|
||||
|
||||
Future<Wallet> restoreFromKeys(String name, String password, String language,
|
||||
int restoreHeight, String address, String viewKey, String spendKey);
|
||||
|
||||
Future<Wallet> openWallet(String name, String password);
|
||||
|
||||
Future<bool> isWalletExit(String name);
|
||||
|
||||
Future remove(WalletDescription wallet);
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
import 'package:cake_wallet/src/domain/common/crypto_amount_format.dart';
|
||||
|
||||
const dashAmountDivider = 100000000;
|
||||
|
||||
double dashAmountToDouble({int amount}) =>
|
||||
cryptoAmountToDouble(amount: amount, divider: dashAmountDivider);
|
||||
@@ -1,6 +0,0 @@
|
||||
import 'package:cake_wallet/src/domain/common/crypto_amount_format.dart';
|
||||
|
||||
const ethereumAmountDivider = 1000000000000000000;
|
||||
|
||||
double ethereumAmountToDouble({num amount}) =>
|
||||
cryptoAmountToDouble(amount: amount, divider: ethereumAmountDivider);
|
||||
@@ -1,163 +0,0 @@
|
||||
import 'dart:convert';
|
||||
import 'package:cake_wallet/src/domain/exchange/trade_not_found_exeption.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
||||
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/exchange_pair.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/exchange_provider.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/limits.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/trade.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/trade_request.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/trade_state.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/changenow/changenow_request.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/exchange_provider_description.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/trade_not_created_exeption.dart';
|
||||
|
||||
class ChangeNowExchangeProvider extends ExchangeProvider {
|
||||
ChangeNowExchangeProvider()
|
||||
: super(
|
||||
pairList: CryptoCurrency.all
|
||||
.map((i) {
|
||||
return CryptoCurrency.all.map((k) {
|
||||
if (i == CryptoCurrency.btc && k == CryptoCurrency.xmr) {
|
||||
return ExchangePair(from: i, to: k, reverse: false);
|
||||
}
|
||||
|
||||
if (i == CryptoCurrency.xmr && k == CryptoCurrency.btc) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return ExchangePair(from: i, to: k, reverse: true);
|
||||
}).where((c) => c != null);
|
||||
})
|
||||
.expand((i) => i)
|
||||
.toList());
|
||||
|
||||
static const apiUri = 'https://changenow.io/api/v1';
|
||||
static const apiKey = secrets.change_now_api_key;
|
||||
static const _exchangeAmountUriSufix = '/exchange-amount/';
|
||||
static const _transactionsUriSufix = '/transactions/';
|
||||
static const _minAmountUriSufix = '/min-amount/';
|
||||
|
||||
@override
|
||||
String get title => 'ChangeNOW';
|
||||
|
||||
@override
|
||||
ExchangeProviderDescription get description =>
|
||||
ExchangeProviderDescription.changeNow;
|
||||
|
||||
@override
|
||||
Future<Limits> fetchLimits({CryptoCurrency from, CryptoCurrency to}) async {
|
||||
final symbol = from.toString() + '_' + to.toString();
|
||||
final url = apiUri + _minAmountUriSufix + symbol;
|
||||
final response = await get(url);
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final min = responseJSON['minAmount'] as double;
|
||||
|
||||
return Limits(min: min, max: null);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Trade> createTrade({TradeRequest request}) async {
|
||||
const url = apiUri + _transactionsUriSufix + apiKey;
|
||||
final _request = request as ChangeNowRequest;
|
||||
final body = {
|
||||
'from': _request.from.toString(),
|
||||
'to': _request.to.toString(),
|
||||
'address': _request.address,
|
||||
'amount': _request.amount,
|
||||
'refundAddress': _request.refundAddress
|
||||
};
|
||||
|
||||
final response = await post(url,
|
||||
headers: {'Content-Type': 'application/json'}, body: json.encode(body));
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
if (response.statusCode == 400) {
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final error = responseJSON['message'] as String;
|
||||
|
||||
throw TradeNotCreatedException(description, description: error);
|
||||
}
|
||||
|
||||
throw TradeNotCreatedException(description);
|
||||
}
|
||||
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final id = responseJSON['id'] as String;
|
||||
final inputAddress = responseJSON['payinAddress'] as String;
|
||||
final refundAddress = responseJSON['refundAddress'] as String;
|
||||
final extraId = responseJSON['payinExtraId'] as String;
|
||||
|
||||
return Trade(
|
||||
id: id,
|
||||
from: _request.from,
|
||||
to: _request.to,
|
||||
provider: description,
|
||||
inputAddress: inputAddress,
|
||||
refundAddress: refundAddress,
|
||||
extraId: extraId,
|
||||
createdAt: DateTime.now(),
|
||||
amount: _request.amount,
|
||||
state: TradeState.created);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Trade> findTradeById({@required String id}) async {
|
||||
final url = apiUri + _transactionsUriSufix + id + '/' + apiKey;
|
||||
final response = await get(url);
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
if (response.statusCode == 400) {
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final error = responseJSON['message'] as String;
|
||||
|
||||
throw TradeNotFoundException(id,
|
||||
provider: description, description: error);
|
||||
}
|
||||
|
||||
throw TradeNotFoundException(id, provider: description);
|
||||
}
|
||||
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final fromCurrency = responseJSON['fromCurrency'] as String;
|
||||
final from = CryptoCurrency.fromString(fromCurrency);
|
||||
final toCurrency = responseJSON['toCurrency'] as String;
|
||||
final to = CryptoCurrency.fromString(toCurrency);
|
||||
final inputAddress = responseJSON['payinAddress'] as String;
|
||||
final expectedSendAmount = responseJSON['expectedSendAmount'].toString();
|
||||
final status = responseJSON['status'] as String;
|
||||
final state = TradeState.deserialize(raw: status);
|
||||
final extraId = responseJSON['payinExtraId'] as String;
|
||||
final outputTransaction = responseJSON['payoutHash'] as String;
|
||||
|
||||
return Trade(
|
||||
id: id,
|
||||
from: from,
|
||||
to: to,
|
||||
provider: description,
|
||||
inputAddress: inputAddress,
|
||||
amount: expectedSendAmount,
|
||||
state: state,
|
||||
extraId: extraId,
|
||||
outputTransaction: outputTransaction);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<double> calculateAmount(
|
||||
{CryptoCurrency from, CryptoCurrency to, double amount}) async {
|
||||
final url = apiUri +
|
||||
_exchangeAmountUriSufix +
|
||||
amount.toString() +
|
||||
'/' +
|
||||
from.toString() +
|
||||
'_' +
|
||||
to.toString();
|
||||
final response = await get(url);
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final estimatedAmount = responseJSON['estimatedAmount'] as double;
|
||||
|
||||
return estimatedAmount;
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/trade_request.dart';
|
||||
|
||||
class ChangeNowRequest extends TradeRequest {
|
||||
ChangeNowRequest(
|
||||
{@required this.from,
|
||||
@required this.to,
|
||||
@required this.address,
|
||||
@required this.amount,
|
||||
@required this.refundAddress});
|
||||
|
||||
CryptoCurrency from;
|
||||
CryptoCurrency to;
|
||||
String address;
|
||||
String amount;
|
||||
String refundAddress;
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
|
||||
|
||||
class ExchangePair {
|
||||
ExchangePair({this.from, this.to, this.reverse = true});
|
||||
|
||||
final CryptoCurrency from;
|
||||
final CryptoCurrency to;
|
||||
final bool reverse;
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/trade_request.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/exchange_pair.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/limits.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/trade.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/exchange_provider_description.dart';
|
||||
|
||||
abstract class ExchangeProvider {
|
||||
ExchangeProvider({this.pairList});
|
||||
|
||||
String get title;
|
||||
List<ExchangePair> pairList;
|
||||
ExchangeProviderDescription description;
|
||||
|
||||
@override
|
||||
String toString() => title;
|
||||
|
||||
Future<Limits> fetchLimits({CryptoCurrency from, CryptoCurrency to});
|
||||
Future<Trade> createTrade({TradeRequest request});
|
||||
Future<Trade> findTradeById({@required String id});
|
||||
Future<double> calculateAmount(
|
||||
{CryptoCurrency from, CryptoCurrency to, double amount});
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
import 'package:cake_wallet/src/domain/common/enumerable_item.dart';
|
||||
|
||||
class ExchangeProviderDescription extends EnumerableItem<int>
|
||||
with Serializable<int> {
|
||||
const ExchangeProviderDescription({String title, int raw})
|
||||
: super(title: title, raw: raw);
|
||||
|
||||
static const xmrto = ExchangeProviderDescription(title: 'XMR.TO', raw: 0);
|
||||
static const changeNow =
|
||||
ExchangeProviderDescription(title: 'ChangeNOW', raw: 1);
|
||||
static const morphToken =
|
||||
ExchangeProviderDescription(title: 'MorphToken', raw: 2);
|
||||
|
||||
static ExchangeProviderDescription deserialize({int raw}) {
|
||||
switch (raw) {
|
||||
case 0:
|
||||
return xmrto;
|
||||
case 1:
|
||||
return changeNow;
|
||||
case 2:
|
||||
return morphToken;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
part 'exchange_template.g.dart';
|
||||
|
||||
@HiveType(typeId: 7)
|
||||
class ExchangeTemplate extends HiveObject {
|
||||
ExchangeTemplate({
|
||||
this.amount,
|
||||
this.depositCurrency,
|
||||
this.receiveCurrency,
|
||||
this.provider,
|
||||
this.depositAddress,
|
||||
this.receiveAddress
|
||||
});
|
||||
|
||||
static const boxName = 'ExchangeTemplate';
|
||||
|
||||
@HiveField(0)
|
||||
String amount;
|
||||
|
||||
@HiveField(1)
|
||||
String depositCurrency;
|
||||
|
||||
@HiveField(2)
|
||||
String receiveCurrency;
|
||||
|
||||
@HiveField(3)
|
||||
String provider;
|
||||
|
||||
@HiveField(4)
|
||||
String depositAddress;
|
||||
|
||||
@HiveField(5)
|
||||
String receiveAddress;
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
class Limits {
|
||||
const Limits({this.min, this.max});
|
||||
|
||||
final double min;
|
||||
final double max;
|
||||
}
|
||||
@@ -1,241 +0,0 @@
|
||||
import 'dart:convert';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/trade_not_found_exeption.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/exchange_pair.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/exchange_provider.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/limits.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/trade.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/trade_request.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/trade_state.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/morphtoken/morphtoken_request.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/exchange_provider_description.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/trade_not_created_exeption.dart';
|
||||
import 'package:cake_wallet/src/domain/monero/monero_amount_format.dart';
|
||||
import 'package:cake_wallet/src/domain/bitcoin/bitcoin_amount_format.dart';
|
||||
import 'package:cake_wallet/src/domain/bitcoin_cash/bitcoin_cash_amount_format.dart';
|
||||
import 'package:cake_wallet/src/domain/dash/dash_amount_format.dart';
|
||||
import 'package:cake_wallet/src/domain/ethereum/ethereum_amount_format.dart';
|
||||
import 'package:cake_wallet/src/domain/litecoin/litecoin_amount_format.dart';
|
||||
|
||||
class MorphTokenExchangeProvider extends ExchangeProvider {
|
||||
MorphTokenExchangeProvider({@required this.trades})
|
||||
: super(
|
||||
pairList: [
|
||||
ExchangePair(from: CryptoCurrency.xmr, to: CryptoCurrency.eth),
|
||||
ExchangePair(from: CryptoCurrency.xmr, to: CryptoCurrency.bch),
|
||||
ExchangePair(from: CryptoCurrency.xmr, to: CryptoCurrency.ltc),
|
||||
ExchangePair(from: CryptoCurrency.xmr, to: CryptoCurrency.dash),
|
||||
|
||||
ExchangePair(from: CryptoCurrency.dash, to: CryptoCurrency.btc),
|
||||
ExchangePair(from: CryptoCurrency.dash, to: CryptoCurrency.eth),
|
||||
ExchangePair(from: CryptoCurrency.dash, to: CryptoCurrency.bch),
|
||||
ExchangePair(from: CryptoCurrency.dash, to: CryptoCurrency.ltc),
|
||||
ExchangePair(from: CryptoCurrency.dash, to: CryptoCurrency.xmr),
|
||||
|
||||
ExchangePair(from: CryptoCurrency.ltc, to: CryptoCurrency.btc),
|
||||
ExchangePair(from: CryptoCurrency.ltc, to: CryptoCurrency.eth),
|
||||
ExchangePair(from: CryptoCurrency.ltc, to: CryptoCurrency.bch),
|
||||
ExchangePair(from: CryptoCurrency.ltc, to: CryptoCurrency.dash),
|
||||
ExchangePair(from: CryptoCurrency.ltc, to: CryptoCurrency.xmr),
|
||||
|
||||
ExchangePair(from: CryptoCurrency.bch, to: CryptoCurrency.btc),
|
||||
ExchangePair(from: CryptoCurrency.bch, to: CryptoCurrency.eth),
|
||||
ExchangePair(from: CryptoCurrency.bch, to: CryptoCurrency.ltc),
|
||||
ExchangePair(from: CryptoCurrency.bch, to: CryptoCurrency.dash),
|
||||
ExchangePair(from: CryptoCurrency.bch, to: CryptoCurrency.xmr),
|
||||
|
||||
ExchangePair(from: CryptoCurrency.eth, to: CryptoCurrency.btc),
|
||||
ExchangePair(from: CryptoCurrency.eth, to: CryptoCurrency.bch),
|
||||
ExchangePair(from: CryptoCurrency.eth, to: CryptoCurrency.ltc),
|
||||
ExchangePair(from: CryptoCurrency.eth, to: CryptoCurrency.dash),
|
||||
ExchangePair(from: CryptoCurrency.eth, to: CryptoCurrency.xmr),
|
||||
|
||||
ExchangePair(from: CryptoCurrency.btc, to: CryptoCurrency.eth),
|
||||
ExchangePair(from: CryptoCurrency.btc, to: CryptoCurrency.bch),
|
||||
ExchangePair(from: CryptoCurrency.btc, to: CryptoCurrency.ltc),
|
||||
ExchangePair(from: CryptoCurrency.btc, to: CryptoCurrency.dash),
|
||||
ExchangePair(from: CryptoCurrency.btc, to: CryptoCurrency.xmr)
|
||||
]);
|
||||
|
||||
Box<Trade> trades;
|
||||
|
||||
static const apiUri = 'https://api.morphtoken.com';
|
||||
static const _morphURISuffix = '/morph';
|
||||
static const _limitsURISuffix = '/limits';
|
||||
static const _ratesURISuffix = '/rates';
|
||||
static const weight = 10000;
|
||||
|
||||
@override
|
||||
String get title => 'MorphToken';
|
||||
|
||||
@override
|
||||
ExchangeProviderDescription get description =>
|
||||
ExchangeProviderDescription.morphToken;
|
||||
|
||||
@override
|
||||
Future<Limits> fetchLimits({CryptoCurrency from, CryptoCurrency to}) async {
|
||||
final url = apiUri + _limitsURISuffix;
|
||||
final headers = {'Content-type': 'application/json'};
|
||||
final body =
|
||||
json.encode({
|
||||
"input": {
|
||||
"asset": from.toString()
|
||||
},
|
||||
"output": [{
|
||||
"asset": to.toString(),
|
||||
"weight": weight
|
||||
}]});
|
||||
final response =
|
||||
await post(url, headers: headers, body: body);
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
|
||||
final min = responseJSON['input']['limits']['min'] as int;
|
||||
int max;
|
||||
double ethMax;
|
||||
|
||||
if (from == CryptoCurrency.eth) {
|
||||
ethMax = responseJSON['input']['limits']['max'] as double;
|
||||
} else {
|
||||
max = responseJSON['input']['limits']['max'] as int;
|
||||
}
|
||||
|
||||
double minFormatted;
|
||||
double maxFormatted;
|
||||
|
||||
switch (from) {
|
||||
case CryptoCurrency.xmr:
|
||||
minFormatted = moneroAmountToDouble(amount: min);
|
||||
maxFormatted = moneroAmountToDouble(amount: max);
|
||||
break;
|
||||
case CryptoCurrency.btc:
|
||||
minFormatted = bitcoinAmountToDouble(amount: min);
|
||||
maxFormatted = bitcoinAmountToDouble(amount: max);
|
||||
break;
|
||||
case CryptoCurrency.bch:
|
||||
minFormatted = bitcoinCashAmountToDouble(amount: min);
|
||||
maxFormatted = bitcoinCashAmountToDouble(amount: max);
|
||||
break;
|
||||
case CryptoCurrency.dash:
|
||||
minFormatted = dashAmountToDouble(amount: min);
|
||||
maxFormatted = dashAmountToDouble(amount: max);
|
||||
break;
|
||||
case CryptoCurrency.eth:
|
||||
minFormatted = ethereumAmountToDouble(amount: min);
|
||||
maxFormatted = ethereumAmountToDouble(amount: ethMax);
|
||||
break;
|
||||
case CryptoCurrency.ltc:
|
||||
minFormatted = litecoinAmountToDouble(amount: min);
|
||||
maxFormatted = litecoinAmountToDouble(amount: max);
|
||||
break;
|
||||
}
|
||||
|
||||
return Limits(min: minFormatted, max: maxFormatted);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Trade> createTrade({TradeRequest request}) async {
|
||||
const url = apiUri + _morphURISuffix;
|
||||
final _request = request as MorphTokenRequest;
|
||||
final body = {
|
||||
"input": {
|
||||
"asset": _request.from.toString(),
|
||||
"refund": _request.refundAddress
|
||||
},
|
||||
"output": [{
|
||||
"asset": _request.to.toString(),
|
||||
"weight": weight,
|
||||
"address": _request.address
|
||||
}],
|
||||
"tag": "cakewallet"
|
||||
};
|
||||
|
||||
final response = await post(url,
|
||||
headers: {'Content-Type': 'application/json'}, body: json.encode(body));
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
if (response.statusCode == 400) {
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final error = responseJSON['description'] as String;
|
||||
|
||||
throw TradeNotCreatedException(description, description: error);
|
||||
}
|
||||
|
||||
throw TradeNotCreatedException(description);
|
||||
}
|
||||
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final id = responseJSON['id'] as String;
|
||||
|
||||
return Trade(
|
||||
id: id,
|
||||
provider: description,
|
||||
from: _request.from,
|
||||
to: _request.to,
|
||||
state: TradeState.created,
|
||||
amount: _request.amount,
|
||||
createdAt: DateTime.now());
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Trade> findTradeById({@required String id}) async {
|
||||
final url = apiUri + _morphURISuffix + '/' + id;
|
||||
final response = await get(url);
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
if (response.statusCode == 400) {
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final error = responseJSON['description'] as String;
|
||||
|
||||
throw TradeNotFoundException(id,
|
||||
provider: description, description: error);
|
||||
}
|
||||
|
||||
throw TradeNotFoundException(id, provider: description);
|
||||
}
|
||||
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final fromCurrency = responseJSON['input']['asset'] as String;
|
||||
final from = CryptoCurrency.fromString(fromCurrency.toLowerCase());
|
||||
final toCurrency = responseJSON['output'][0]['asset'] as String;
|
||||
final to = CryptoCurrency.fromString(toCurrency.toLowerCase());
|
||||
final inputAddress = responseJSON['input']['deposit_address'] as String;
|
||||
final status = responseJSON['state'] as String;
|
||||
final state = TradeState.deserialize(raw: status.toLowerCase());
|
||||
|
||||
String amount = "";
|
||||
for (final trade in trades.values) {
|
||||
if (trade.id == id) {
|
||||
amount = trade.amount;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return Trade(
|
||||
id: id,
|
||||
from: from,
|
||||
to: to,
|
||||
provider: description,
|
||||
inputAddress: inputAddress,
|
||||
amount: amount,
|
||||
state: state);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<double> calculateAmount(
|
||||
{CryptoCurrency from, CryptoCurrency to, double amount}) async {
|
||||
final url = apiUri + _ratesURISuffix;
|
||||
final response = await get(url);
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final rate = responseJSON['data'][from.toString()][to.toString()] as String;
|
||||
|
||||
try {
|
||||
final estimatedAmount = double.parse(rate) * amount;
|
||||
return estimatedAmount;
|
||||
} catch(e) {
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/trade_request.dart';
|
||||
|
||||
class MorphTokenRequest extends TradeRequest {
|
||||
MorphTokenRequest(
|
||||
{@required this.from,
|
||||
@required this.to,
|
||||
@required this.address,
|
||||
@required this.amount,
|
||||
@required this.refundAddress});
|
||||
|
||||
CryptoCurrency from;
|
||||
CryptoCurrency to;
|
||||
String address;
|
||||
String amount;
|
||||
String refundAddress;
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/exchange_provider_description.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/trade_state.dart';
|
||||
import 'package:cake_wallet/src/domain/common/format_amount.dart';
|
||||
|
||||
part 'trade.g.dart';
|
||||
|
||||
@HiveType(typeId: 3)
|
||||
class Trade extends HiveObject {
|
||||
Trade(
|
||||
{this.id,
|
||||
ExchangeProviderDescription provider,
|
||||
CryptoCurrency from,
|
||||
CryptoCurrency to,
|
||||
TradeState state,
|
||||
this.createdAt,
|
||||
this.expiredAt,
|
||||
this.amount,
|
||||
this.inputAddress,
|
||||
this.extraId,
|
||||
this.outputTransaction,
|
||||
this.refundAddress,
|
||||
this.walletId})
|
||||
: providerRaw = provider?.raw,
|
||||
fromRaw = from?.raw,
|
||||
toRaw = to?.raw,
|
||||
stateRaw = state?.raw;
|
||||
|
||||
static const boxName = 'Trades';
|
||||
|
||||
@HiveField(0)
|
||||
String id;
|
||||
|
||||
@HiveField(1)
|
||||
int providerRaw;
|
||||
|
||||
ExchangeProviderDescription get provider =>
|
||||
ExchangeProviderDescription.deserialize(raw: providerRaw);
|
||||
|
||||
@HiveField(2)
|
||||
int fromRaw;
|
||||
|
||||
CryptoCurrency get from => CryptoCurrency.deserialize(raw: fromRaw);
|
||||
|
||||
@HiveField(3)
|
||||
int toRaw;
|
||||
|
||||
CryptoCurrency get to => CryptoCurrency.deserialize(raw: toRaw);
|
||||
|
||||
@HiveField(4)
|
||||
String stateRaw;
|
||||
|
||||
TradeState get state => TradeState.deserialize(raw: stateRaw);
|
||||
|
||||
@HiveField(5)
|
||||
DateTime createdAt;
|
||||
|
||||
@HiveField(6)
|
||||
DateTime expiredAt;
|
||||
|
||||
@HiveField(7)
|
||||
String amount;
|
||||
|
||||
@HiveField(8)
|
||||
String inputAddress;
|
||||
|
||||
@HiveField(9)
|
||||
String extraId;
|
||||
|
||||
@HiveField(10)
|
||||
String outputTransaction;
|
||||
|
||||
@HiveField(11)
|
||||
String refundAddress;
|
||||
|
||||
@HiveField(12)
|
||||
String walletId;
|
||||
|
||||
static Trade fromMap(Map map) {
|
||||
return Trade(
|
||||
id: map['id'] as String,
|
||||
provider: ExchangeProviderDescription.deserialize(
|
||||
raw: map['provider'] as int),
|
||||
from: CryptoCurrency.deserialize(raw: map['input'] as int),
|
||||
to: CryptoCurrency.deserialize(raw: map['output'] as int),
|
||||
createdAt: map['date'] != null
|
||||
? DateTime.fromMillisecondsSinceEpoch(map['date'] as int)
|
||||
: null,
|
||||
amount: map['amount'] as String,
|
||||
walletId: map['wallet_id'] as String);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return <String, dynamic>{
|
||||
'id': id,
|
||||
'provider': provider.serialize(),
|
||||
'input': from.serialize(),
|
||||
'output': to.serialize(),
|
||||
'date': createdAt != null ? createdAt.millisecondsSinceEpoch : null,
|
||||
'amount': amount,
|
||||
'wallet_id': walletId
|
||||
};
|
||||
}
|
||||
|
||||
String amountFormatted() => formatAmount(amount);
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
import 'package:cake_wallet/src/domain/exchange/exchange_provider_description.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
|
||||
class TradeNotCreatedException implements Exception {
|
||||
TradeNotCreatedException(this.provider, {this.description = ''});
|
||||
|
||||
ExchangeProviderDescription provider;
|
||||
String description;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
var text = provider != null
|
||||
? S.current.trade_for_not_created(provider.title)
|
||||
: S.current.trade_not_created;
|
||||
text += ' $description';
|
||||
|
||||
return text;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import 'package:cake_wallet/src/domain/exchange/exchange_provider_description.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
|
||||
class TradeNotFoundException implements Exception {
|
||||
TradeNotFoundException(this.tradeId, {this.provider, this.description = ''});
|
||||
|
||||
String tradeId;
|
||||
ExchangeProviderDescription provider;
|
||||
String description;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
var text = tradeId != null && provider != null
|
||||
? S.current.trade_id_not_found(tradeId, provider.title)
|
||||
: S.current.trade_not_found;
|
||||
text += ' $description';
|
||||
|
||||
return text;
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
abstract class TradeRequest {}
|
||||
@@ -1,72 +0,0 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cake_wallet/src/domain/common/enumerable_item.dart';
|
||||
|
||||
class TradeState extends EnumerableItem<String> with Serializable<String> {
|
||||
const TradeState({@required String raw, @required String title})
|
||||
: super(raw: raw, title: title);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => other is TradeState && other.raw == raw;
|
||||
|
||||
static const pending = TradeState(raw: 'pending', title: 'Pending');
|
||||
static const confirming = TradeState(raw: 'confirming', title: 'Confirming');
|
||||
static const trading = TradeState(raw: 'trading', title: 'Trading');
|
||||
static const traded = TradeState(raw: 'traded', title: 'Traded');
|
||||
static const complete = TradeState(raw: 'complete', title: 'Complete');
|
||||
static const toBeCreated =
|
||||
TradeState(raw: 'TO_BE_CREATED', title: 'To be created');
|
||||
static const unpaid = TradeState(raw: 'UNPAID', title: 'Unpaid');
|
||||
static const underpaid = TradeState(raw: 'UNDERPAID', title: 'Underpaid');
|
||||
static const paidUnconfirmed =
|
||||
TradeState(raw: 'PAID_UNCONFIRMED', title: 'Paid unconfirmed');
|
||||
static const paid = TradeState(raw: 'PAID', title: 'Paid');
|
||||
static const btcSent = TradeState(raw: 'BTC_SENT', title: 'Btc sent');
|
||||
static const timeout = TradeState(raw: 'TIMED_OUT', title: 'Timeout');
|
||||
static const notFound = TradeState(raw: 'NOT_FOUND', title: 'Not found');
|
||||
static const created = TradeState(raw: 'created', title: 'Created');
|
||||
static const finished = TradeState(raw: 'finished', title: 'Finished');
|
||||
static const waiting = TradeState(raw: 'waiting', title: 'Waiting');
|
||||
static const processing = TradeState(raw: 'processing', title: 'Processing');
|
||||
|
||||
static TradeState deserialize({String raw}) {
|
||||
switch (raw) {
|
||||
case 'pending':
|
||||
return pending;
|
||||
case 'confirming':
|
||||
return confirming;
|
||||
case 'trading':
|
||||
return trading;
|
||||
case 'traded':
|
||||
return traded;
|
||||
case 'complete':
|
||||
return complete;
|
||||
case 'TO_BE_CREATED':
|
||||
return toBeCreated;
|
||||
case 'UNPAID':
|
||||
return unpaid;
|
||||
case 'UNDERPAID':
|
||||
return underpaid;
|
||||
case 'PAID_UNCONFIRMED':
|
||||
return paidUnconfirmed;
|
||||
case 'PAID':
|
||||
return paid;
|
||||
case 'BTC_SENT':
|
||||
return btcSent;
|
||||
case 'TIMED_OUT':
|
||||
return timeout;
|
||||
case 'created':
|
||||
return created;
|
||||
case 'finished':
|
||||
return finished;
|
||||
case 'waiting':
|
||||
return waiting;
|
||||
case 'processing':
|
||||
return processing;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => raw.hashCode ^ title.hashCode;
|
||||
}
|
||||
@@ -1,198 +0,0 @@
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/exchange_pair.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/exchange_provider.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/limits.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/trade.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/trade_request.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/trade_state.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/xmrto/xmrto_trade_request.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/trade_not_created_exeption.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/exchange_provider_description.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/trade_not_found_exeption.dart';
|
||||
|
||||
class XMRTOExchangeProvider extends ExchangeProvider {
|
||||
XMRTOExchangeProvider()
|
||||
: super(pairList: [
|
||||
ExchangePair(
|
||||
from: CryptoCurrency.xmr, to: CryptoCurrency.btc, reverse: false)
|
||||
]);
|
||||
|
||||
static const userAgent = 'CakeWallet/XMR iOS';
|
||||
static const originalApiUri = 'https://xmr.to/api/v2/xmr2btc';
|
||||
static const proxyApiUri = 'https://xmrproxy.net/api/v2/xmr2btc';
|
||||
static const _orderParameterUriSufix = '/order_parameter_query';
|
||||
static const _orderStatusUriSufix = '/order_status_query/';
|
||||
static const _orderCreateUriSufix = '/order_create/';
|
||||
static String _apiUri = '';
|
||||
|
||||
static Future<String> getApiUri() async {
|
||||
if (_apiUri != null && _apiUri.isNotEmpty) {
|
||||
return _apiUri;
|
||||
}
|
||||
|
||||
const url = originalApiUri + _orderParameterUriSufix;
|
||||
final response =
|
||||
await get(url, headers: {'Content-Type': 'application/json'});
|
||||
_apiUri = response.statusCode == 403 ? proxyApiUri : originalApiUri;
|
||||
|
||||
return _apiUri;
|
||||
}
|
||||
|
||||
@override
|
||||
String get title => 'XMR.TO';
|
||||
|
||||
@override
|
||||
ExchangeProviderDescription get description =>
|
||||
ExchangeProviderDescription.xmrto;
|
||||
|
||||
double _rate = 0;
|
||||
|
||||
@override
|
||||
Future<Limits> fetchLimits({CryptoCurrency from, CryptoCurrency to}) async {
|
||||
final url = await getApiUri() + _orderParameterUriSufix;
|
||||
final response = await get(url);
|
||||
final correction = 0.001;
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
return Limits(min: 0, max: 0);
|
||||
}
|
||||
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
double min = responseJSON['lower_limit'] as double;
|
||||
double max = responseJSON['upper_limit'] as double;
|
||||
final price = responseJSON['price'] as double;
|
||||
|
||||
if (price > 0) {
|
||||
try {
|
||||
min = min/price + correction;
|
||||
min = _limitsFormat(min);
|
||||
max = max/price - correction;
|
||||
max = _limitsFormat(max);
|
||||
} catch (e) {
|
||||
min = 0;
|
||||
max = 0;
|
||||
}
|
||||
} else {
|
||||
min = 0;
|
||||
max = 0;
|
||||
}
|
||||
|
||||
return Limits(min: min, max: max);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Trade> createTrade({TradeRequest request}) async {
|
||||
final _request = request as XMRTOTradeRequest;
|
||||
final url = await getApiUri() + _orderCreateUriSufix;
|
||||
final body = {
|
||||
'xmr_amount': _request.amount,
|
||||
'btc_dest_address': _request.address
|
||||
};
|
||||
final response = await post(url,
|
||||
headers: {'Content-Type': 'application/json'}, body: json.encode(body));
|
||||
|
||||
if (response.statusCode != 201) {
|
||||
if (response.statusCode == 400) {
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final error = responseJSON['error_msg'] as String;
|
||||
|
||||
throw TradeNotCreatedException(description, description: error);
|
||||
}
|
||||
|
||||
throw TradeNotCreatedException(description);
|
||||
}
|
||||
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final uuid = responseJSON["uuid"] as String;
|
||||
|
||||
return Trade(
|
||||
id: uuid,
|
||||
provider: description,
|
||||
from: _request.from,
|
||||
to: _request.to,
|
||||
state: TradeState.created,
|
||||
amount: _request.amount,
|
||||
createdAt: DateTime.now());
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Trade> findTradeById({@required String id}) async {
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'User-Agent': userAgent
|
||||
};
|
||||
final url = await getApiUri() + _orderStatusUriSufix;
|
||||
final body = {'uuid': id};
|
||||
final response = await post(url, headers: headers, body: json.encode(body));
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
if (response.statusCode == 400) {
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final error = responseJSON['error_msg'] as String;
|
||||
|
||||
throw TradeNotFoundException(id,
|
||||
provider: description, description: error);
|
||||
}
|
||||
|
||||
throw TradeNotFoundException(id, provider: description);
|
||||
}
|
||||
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final address = responseJSON['xmr_receiving_integrated_address'] as String;
|
||||
final paymentId = responseJSON['xmr_required_payment_id_short'] as String;
|
||||
final amount = responseJSON['xmr_amount_total'].toString();
|
||||
final stateRaw = responseJSON['state'] as String;
|
||||
final expiredAtRaw = responseJSON['expires_at'] as String;
|
||||
final expiredAt = DateTime.parse(expiredAtRaw).toLocal();
|
||||
final outputTransaction = responseJSON['btc_transaction_id'] as String;
|
||||
final state = TradeState.deserialize(raw: stateRaw);
|
||||
|
||||
return Trade(
|
||||
id: id,
|
||||
provider: description,
|
||||
from: CryptoCurrency.xmr,
|
||||
to: CryptoCurrency.btc,
|
||||
inputAddress: address,
|
||||
extraId: paymentId,
|
||||
expiredAt: expiredAt,
|
||||
amount: amount,
|
||||
state: state,
|
||||
outputTransaction: outputTransaction);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<double> calculateAmount(
|
||||
{CryptoCurrency from, CryptoCurrency to, double amount}) async {
|
||||
if (from != CryptoCurrency.xmr && to != CryptoCurrency.btc) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (_rate == null || _rate == 0) {
|
||||
_rate = await _fetchRates();
|
||||
}
|
||||
|
||||
final double result = _rate * amount;
|
||||
|
||||
return double.parse(result.toStringAsFixed(12));
|
||||
}
|
||||
|
||||
Future<double> _fetchRates() async {
|
||||
try {
|
||||
final url = await getApiUri() + _orderParameterUriSufix;
|
||||
final response =
|
||||
await get(url, headers: {'Content-Type': 'application/json'});
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final price = responseJSON['price'] as double;
|
||||
|
||||
return price;
|
||||
} catch (e) {
|
||||
print(e.toString());
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
double _limitsFormat(double limit) => double.parse(limit.toStringAsFixed(3));
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/trade_request.dart';
|
||||
|
||||
class XMRTOTradeRequest extends TradeRequest {
|
||||
XMRTOTradeRequest(
|
||||
{@required this.from,
|
||||
@required this.to,
|
||||
@required this.amount,
|
||||
@required this.address,
|
||||
@required this.refundAddress});
|
||||
|
||||
final CryptoCurrency from;
|
||||
final CryptoCurrency to;
|
||||
final String amount;
|
||||
final String address;
|
||||
final String refundAddress;
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
import 'package:cake_wallet/src/domain/common/crypto_amount_format.dart';
|
||||
|
||||
const litecoinAmountDivider = 100000000;
|
||||
|
||||
double litecoinAmountToDouble({int amount}) =>
|
||||
cryptoAmountToDouble(amount: amount, divider: litecoinAmountDivider);
|
||||
@@ -1,16 +0,0 @@
|
||||
import 'package:cw_monero/structs/account_row.dart';
|
||||
|
||||
class Account {
|
||||
Account({this.id, this.label});
|
||||
|
||||
Account.fromMap(Map map)
|
||||
: this.id = map['id'] == null ? 0 : int.parse(map['id'] as String),
|
||||
this.label = (map['label'] ?? '') as String;
|
||||
|
||||
Account.fromRow(AccountRow row)
|
||||
: this.id = row.getId(),
|
||||
this.label = row.getLabel();
|
||||
|
||||
final int id;
|
||||
final String label;
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
import 'package:rxdart/rxdart.dart';
|
||||
import 'package:cw_monero/account_list.dart' as account_list;
|
||||
import 'package:cake_wallet/src/domain/monero/account.dart';
|
||||
|
||||
class AccountList {
|
||||
AccountList() {
|
||||
_isRefreshing = false;
|
||||
_isUpdating = false;
|
||||
_accounts = BehaviorSubject<List<Account>>();
|
||||
}
|
||||
|
||||
Observable<List<Account>> get accounts => _accounts.stream;
|
||||
|
||||
BehaviorSubject<List<Account>> _accounts;
|
||||
bool _isRefreshing;
|
||||
bool _isUpdating;
|
||||
|
||||
Future update() async {
|
||||
if (_isUpdating) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
_isUpdating = true;
|
||||
refresh();
|
||||
final accounts = getAll();
|
||||
_accounts.add(accounts);
|
||||
_isUpdating = false;
|
||||
} catch (e) {
|
||||
_isUpdating = false;
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
List<Account> getAll() {
|
||||
return account_list
|
||||
.getAllAccount()
|
||||
.map((accountRow) => Account.fromRow(accountRow))
|
||||
.toList();
|
||||
}
|
||||
|
||||
Future addAccount({String label}) async {
|
||||
await account_list.addAccount(label: label);
|
||||
await update();
|
||||
}
|
||||
|
||||
Future setLabelSubaddress({int accountIndex, String label}) async {
|
||||
await account_list.setLabelForAccount(
|
||||
accountIndex: accountIndex, label: label);
|
||||
await update();
|
||||
}
|
||||
|
||||
void refresh() {
|
||||
if (_isRefreshing) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
_isRefreshing = true;
|
||||
account_list.refreshAccounts();
|
||||
_isRefreshing = false;
|
||||
} catch (e) {
|
||||
_isRefreshing = false;
|
||||
print(e);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
final dateFormat = DateFormat('yyyy-MM');
|
||||
final dates = {
|
||||
"2014-5": 18844,
|
||||
"2014-6": 65406,
|
||||
"2014-7": 108882,
|
||||
"2014-8": 153594,
|
||||
"2014-9": 198072,
|
||||
"2014-10": 241088,
|
||||
"2014-11": 285305,
|
||||
"2014-12": 328069,
|
||||
"2015-1": 372369,
|
||||
"2015-2": 416505,
|
||||
"2015-3": 456631,
|
||||
"2015-4": 501084,
|
||||
"2015-5": 543973,
|
||||
"2015-6": 588326,
|
||||
"2015-7": 631187,
|
||||
"2015-8": 675484,
|
||||
"2015-9": 719725,
|
||||
"2015-10": 762463,
|
||||
"2015-11": 806528,
|
||||
"2015-12": 849041,
|
||||
"2016-1": 892866,
|
||||
"2016-2": 936736,
|
||||
"2016-3": 977691,
|
||||
"2016-4": 1015848,
|
||||
"2016-5": 1037417,
|
||||
"2016-6": 1059651,
|
||||
"2016-7": 1081269,
|
||||
"2016-8": 1103630,
|
||||
"2016-9": 1125983,
|
||||
"2016-10": 1147617,
|
||||
"2016-11": 1169779,
|
||||
"2016-12": 1191402,
|
||||
"2017-1": 1213861,
|
||||
"2017-2": 1236197,
|
||||
"2017-3": 1256358,
|
||||
"2017-4": 1278622,
|
||||
"2017-5": 1300239,
|
||||
"2017-6": 1322564,
|
||||
"2017-7": 1344225,
|
||||
"2017-8": 1366664,
|
||||
"2017-9": 1389113,
|
||||
"2017-10": 1410738,
|
||||
"2017-11": 1433039,
|
||||
"2017-12": 1454639,
|
||||
"2018-1": 1477201,
|
||||
"2018-2": 1499599,
|
||||
"2018-3": 1519796,
|
||||
"2018-4": 1542067,
|
||||
"2018-5": 1562861,
|
||||
"2018-6": 1585135,
|
||||
"2018-7": 1606715,
|
||||
"2018-8": 1629017,
|
||||
"2018-9": 1651347,
|
||||
"2018-10": 1673031,
|
||||
"2018-11": 1695128,
|
||||
"2018-12": 1716687,
|
||||
"2019-1": 1738923,
|
||||
"2019-2": 1761435,
|
||||
"2019-3": 1781681,
|
||||
"2019-4": 1803081,
|
||||
"2019-5": 1824671,
|
||||
"2019-6": 1847005,
|
||||
"2019-7": 1868590,
|
||||
"2019-8": 1888590,
|
||||
"2019-9": 1898590,
|
||||
};
|
||||
|
||||
int getHeigthByDate({DateTime date}) {
|
||||
final raw = '${date.year}' + '-' + '${date.month}';
|
||||
var endHeight = dates[raw] ?? 0;
|
||||
int preLastYear = date.year;
|
||||
int preLastMonth = date.month - 1;
|
||||
|
||||
if (endHeight <= 0) {
|
||||
endHeight = dates.values.toList()[dates.length - 1];
|
||||
final preLastDate =
|
||||
dateFormat.parse(dates.keys.elementAt(dates.keys.length - 2));
|
||||
preLastYear = preLastDate.year;
|
||||
preLastMonth = preLastDate.month;
|
||||
} else {
|
||||
preLastYear = date.year;
|
||||
preLastMonth = date.month - 1;
|
||||
}
|
||||
|
||||
if (preLastMonth <= 0) {
|
||||
preLastMonth = 12;
|
||||
preLastYear -= 1;
|
||||
}
|
||||
|
||||
final startRaw = '$preLastYear' + '-' + '$preLastMonth';
|
||||
final startHeight = dates[startRaw];
|
||||
final diff = endHeight - startHeight;
|
||||
final heightPerDay = diff / 30;
|
||||
final daysHeight = date.day * heightPerDay.round();
|
||||
final height = endHeight + daysHeight;
|
||||
|
||||
return height;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,15 +0,0 @@
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:cake_wallet/src/domain/common/crypto_amount_format.dart';
|
||||
|
||||
const moneroAmountLength = 12;
|
||||
const moneroAmountDivider = 1000000000000;
|
||||
final moneroAmountFormat = NumberFormat()
|
||||
..maximumFractionDigits = moneroAmountLength
|
||||
..minimumFractionDigits = 1;
|
||||
|
||||
String moneroAmountToString({int amount}) =>
|
||||
moneroAmountFormat.format(cryptoAmountToDouble(amount: amount, divider: moneroAmountDivider));
|
||||
|
||||
double moneroAmountToDouble({int amount}) => cryptoAmountToDouble(amount: amount, divider: moneroAmountDivider);
|
||||
|
||||
int moneroParseAmount({String amount}) => moneroAmountFormat.parse(amount).toInt();
|
||||
@@ -1,9 +0,0 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cake_wallet/src/domain/common/balance.dart';
|
||||
|
||||
class MoneroBalance extends Balance {
|
||||
MoneroBalance({@required this.fullBalance, @required this.unlockedBalance});
|
||||
|
||||
final String fullBalance;
|
||||
final String unlockedBalance;
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import 'package:cake_wallet/src/domain/common/transaction_creation_credentials.dart';
|
||||
import 'package:cake_wallet/src/domain/common/transaction_priority.dart';
|
||||
|
||||
class MoneroTransactionCreationCredentials
|
||||
extends TransactionCreationCredentials {
|
||||
MoneroTransactionCreationCredentials(
|
||||
{this.address, this.paymentId, this.priority, this.amount});
|
||||
|
||||
final String address;
|
||||
final String paymentId;
|
||||
final String amount;
|
||||
final TransactionPriority priority;
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
import 'dart:core';
|
||||
import 'package:cake_wallet/src/domain/monero/monero_transaction_info.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:rxdart/rxdart.dart';
|
||||
import 'package:cw_monero/transaction_history.dart'
|
||||
as monero_transaction_history;
|
||||
import 'package:cake_wallet/src/domain/common/transaction_history.dart';
|
||||
import 'package:cake_wallet/src/domain/common/transaction_info.dart';
|
||||
|
||||
List<TransactionInfo> _getAllTransactions(dynamic _) =>
|
||||
monero_transaction_history
|
||||
.getAllTransations()
|
||||
.map((row) => MoneroTransactionInfo.fromRow(row))
|
||||
.toList();
|
||||
|
||||
class MoneroTransactionHistory extends TransactionHistory {
|
||||
MoneroTransactionHistory()
|
||||
: _transactions = BehaviorSubject<List<TransactionInfo>>.seeded([]);
|
||||
|
||||
@override
|
||||
Observable<List<TransactionInfo>> get transactions => _transactions.stream;
|
||||
|
||||
final BehaviorSubject<List<TransactionInfo>> _transactions;
|
||||
bool _isUpdating = false;
|
||||
bool _isRefreshing = false;
|
||||
bool _needToCheckForRefresh = false;
|
||||
|
||||
@override
|
||||
Future update() async {
|
||||
if (_isUpdating) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
_isUpdating = true;
|
||||
_transactions.value = await getAll(force: true);
|
||||
_isUpdating = false;
|
||||
|
||||
if (!_needToCheckForRefresh) {
|
||||
_needToCheckForRefresh = true;
|
||||
}
|
||||
} catch (e) {
|
||||
_isUpdating = false;
|
||||
print(e);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<TransactionInfo>> getAll({bool force = false}) async {
|
||||
await refresh();
|
||||
return _getAllTransactions(null);
|
||||
}
|
||||
|
||||
Future refresh() async {
|
||||
if (_isRefreshing) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
_isRefreshing = true;
|
||||
monero_transaction_history.refreshTransactions();
|
||||
_isRefreshing = false;
|
||||
} on PlatformException catch (e) {
|
||||
_isRefreshing = false;
|
||||
print(e);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
import 'package:cake_wallet/src/domain/common/transaction_info.dart';
|
||||
import 'package:cake_wallet/src/domain/monero/monero_amount_format.dart';
|
||||
import 'package:cw_monero/structs/transaction_info_row.dart';
|
||||
import 'package:cake_wallet/src/domain/common/parseBoolFromString.dart';
|
||||
import 'package:cake_wallet/src/domain/common/transaction_direction.dart';
|
||||
import 'package:cake_wallet/src/domain/common/format_amount.dart';
|
||||
|
||||
class MoneroTransactionInfo extends TransactionInfo {
|
||||
MoneroTransactionInfo(this.id, this.height, this.direction, this.date,
|
||||
this.isPending, this.amount, this.accountIndex);
|
||||
|
||||
MoneroTransactionInfo.fromMap(Map map)
|
||||
: id = (map['hash'] ?? '') as String,
|
||||
height = (map['height'] ?? 0) as int,
|
||||
direction =
|
||||
parseTransactionDirectionFromNumber(map['direction'] as String) ??
|
||||
TransactionDirection.incoming,
|
||||
date = DateTime.fromMillisecondsSinceEpoch(
|
||||
(int.parse(map['timestamp'] as String) ?? 0) * 1000),
|
||||
isPending = parseBoolFromString(map['isPending'] as String),
|
||||
amount = map['amount'] as int,
|
||||
accountIndex = int.parse(map['accountIndex'] as String);
|
||||
|
||||
MoneroTransactionInfo.fromRow(TransactionInfoRow row)
|
||||
: id = row.getHash(),
|
||||
height = row.blockHeight,
|
||||
direction = parseTransactionDirectionFromInt(row.direction) ??
|
||||
TransactionDirection.incoming,
|
||||
date = DateTime.fromMillisecondsSinceEpoch(row.getDatetime() * 1000),
|
||||
isPending = row.isPending != 0,
|
||||
amount = row.getAmount(),
|
||||
accountIndex = row.subaddrAccount;
|
||||
|
||||
final String id;
|
||||
final int height;
|
||||
final TransactionDirection direction;
|
||||
final DateTime date;
|
||||
final int accountIndex;
|
||||
final bool isPending;
|
||||
final int amount;
|
||||
String recipientAddress;
|
||||
|
||||
String _fiatAmount;
|
||||
|
||||
String amountFormatted() => '${formatAmount(moneroAmountToString(amount: amount))} XMR';
|
||||
|
||||
String fiatAmount() => _fiatAmount ?? '';
|
||||
|
||||
void changeFiatAmount(String amount) => _fiatAmount = formatAmount(amount);
|
||||
}
|
||||
@@ -1,421 +0,0 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:rxdart/rxdart.dart';
|
||||
import 'package:cw_monero/wallet.dart' as monero_wallet;
|
||||
import 'package:cw_monero/transaction_history.dart' as transaction_history;
|
||||
import 'package:cake_wallet/src/domain/common/wallet_info.dart';
|
||||
import 'package:cake_wallet/src/domain/common/wallet.dart';
|
||||
import 'package:cake_wallet/src/domain/common/sync_status.dart';
|
||||
import 'package:cake_wallet/src/domain/common/transaction_history.dart';
|
||||
import 'package:cake_wallet/src/domain/common/transaction_creation_credentials.dart';
|
||||
import 'package:cake_wallet/src/domain/common/pending_transaction.dart';
|
||||
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
|
||||
import 'package:cake_wallet/src/domain/common/node.dart';
|
||||
import 'package:cake_wallet/src/domain/monero/monero_amount_format.dart';
|
||||
import 'package:cake_wallet/src/domain/monero/account.dart';
|
||||
import 'package:cake_wallet/src/domain/monero/account_list.dart';
|
||||
import 'package:cake_wallet/src/domain/monero/subaddress_list.dart';
|
||||
import 'package:cake_wallet/src/domain/monero/monero_transaction_creation_credentials.dart';
|
||||
import 'package:cake_wallet/src/domain/monero/monero_transaction_history.dart';
|
||||
import 'package:cake_wallet/src/domain/monero/subaddress.dart';
|
||||
import 'package:cake_wallet/src/domain/common/balance.dart';
|
||||
import 'package:cake_wallet/src/domain/monero/monero_balance.dart';
|
||||
|
||||
const moneroBlockSize = 1000;
|
||||
|
||||
class MoneroWallet extends Wallet {
|
||||
MoneroWallet({this.walletInfoSource, this.walletInfo}) {
|
||||
_cachedBlockchainHeight = 0;
|
||||
_isSaving = false;
|
||||
_lastSaveTime = 0;
|
||||
_lastRefreshTime = 0;
|
||||
_refreshHeight = 0;
|
||||
_lastSyncHeight = 0;
|
||||
_name = BehaviorSubject<String>();
|
||||
_address = BehaviorSubject<String>();
|
||||
_syncStatus = BehaviorSubject<SyncStatus>();
|
||||
_onBalanceChange = BehaviorSubject<MoneroBalance>();
|
||||
_account = BehaviorSubject<Account>()..add(Account(id: 0));
|
||||
_subaddress = BehaviorSubject<Subaddress>();
|
||||
setListeners();
|
||||
}
|
||||
|
||||
static Future<MoneroWallet> createdWallet(
|
||||
{Box<WalletInfo> walletInfoSource,
|
||||
String name,
|
||||
bool isRecovery = false,
|
||||
int restoreHeight = 0}) async {
|
||||
// const type = WalletType.monero;
|
||||
// final id = walletTypeToString(type).toLowerCase() + '_' + name;
|
||||
// final walletInfo = WalletInfo(
|
||||
// id: id,
|
||||
// name: name,
|
||||
// type: type,
|
||||
// isRecovery: isRecovery,
|
||||
// restoreHeight: restoreHeight);
|
||||
// await walletInfoSource.add(walletInfo);
|
||||
|
||||
// return await configured(
|
||||
// walletInfo: walletInfo, walletInfoSource: walletInfoSource);
|
||||
return null;
|
||||
}
|
||||
|
||||
static Future<MoneroWallet> load(
|
||||
Box<WalletInfo> walletInfoSource, String name, WalletType type) async {
|
||||
final id = walletTypeToString(type).toLowerCase() + '_' + name;
|
||||
final walletInfo = walletInfoSource.values
|
||||
.firstWhere((info) => info.id == id, orElse: () => null);
|
||||
return await configured(
|
||||
walletInfoSource: walletInfoSource, walletInfo: walletInfo);
|
||||
}
|
||||
|
||||
static Future<MoneroWallet> configured(
|
||||
{@required Box<WalletInfo> walletInfoSource,
|
||||
@required WalletInfo walletInfo}) async {
|
||||
final wallet = MoneroWallet(
|
||||
walletInfoSource: walletInfoSource, walletInfo: walletInfo);
|
||||
|
||||
if (walletInfo.isRecovery) {
|
||||
wallet.setRecoveringFromSeed();
|
||||
|
||||
if (walletInfo.restoreHeight != null) {
|
||||
wallet.setRefreshFromBlockHeight(height: walletInfo.restoreHeight);
|
||||
}
|
||||
}
|
||||
|
||||
return wallet;
|
||||
}
|
||||
|
||||
@override
|
||||
String get address => _address.value;
|
||||
|
||||
@override
|
||||
String get name => _name.value;
|
||||
|
||||
@override
|
||||
WalletType getType() => WalletType.monero;
|
||||
|
||||
@override
|
||||
Observable<SyncStatus> get syncStatus => _syncStatus.stream;
|
||||
|
||||
@override
|
||||
Observable<Balance> get onBalanceChange => _onBalanceChange.stream;
|
||||
|
||||
@override
|
||||
Observable<String> get onNameChange => _name.stream;
|
||||
|
||||
@override
|
||||
Observable<String> get onAddressChange => _address.stream;
|
||||
|
||||
Observable<Account> get onAccountChange => _account.stream;
|
||||
|
||||
Observable<Subaddress> get subaddress => _subaddress.stream;
|
||||
|
||||
bool get isRecovery => walletInfo.isRecovery;
|
||||
|
||||
Account get account => _account.value;
|
||||
|
||||
Box<WalletInfo> walletInfoSource;
|
||||
WalletInfo walletInfo;
|
||||
|
||||
BehaviorSubject<Account> _account;
|
||||
BehaviorSubject<MoneroBalance> _onBalanceChange;
|
||||
BehaviorSubject<SyncStatus> _syncStatus;
|
||||
BehaviorSubject<String> _name;
|
||||
BehaviorSubject<String> _address;
|
||||
BehaviorSubject<Subaddress> _subaddress;
|
||||
int _cachedBlockchainHeight;
|
||||
bool _isSaving;
|
||||
int _lastSaveTime;
|
||||
int _lastRefreshTime;
|
||||
int _refreshHeight;
|
||||
int _lastSyncHeight;
|
||||
|
||||
TransactionHistory _cachedTransactionHistory;
|
||||
SubaddressList _cachedSubaddressList;
|
||||
AccountList _cachedAccountList;
|
||||
|
||||
@override
|
||||
Future updateInfo() async {
|
||||
_name.value = await getName();
|
||||
final acccountList = getAccountList()..refresh();
|
||||
_account.value = acccountList.getAll().first;
|
||||
final subaddressList = getSubaddress();
|
||||
subaddressList.refresh(
|
||||
accountIndex: _account.value != null ? _account.value.id : 0);
|
||||
final subaddresses = subaddressList.getAll();
|
||||
_subaddress.value = subaddresses.first;
|
||||
_address.value = await getAddress();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> getFilename() async => monero_wallet.getFilename();
|
||||
|
||||
@override
|
||||
Future<String> getName() async => getFilename()
|
||||
.then((filename) => filename.split('/'))
|
||||
.then((splitted) => splitted.last);
|
||||
|
||||
@override
|
||||
Future<String> getAddress() async => monero_wallet.getAddress(
|
||||
accountIndex: _account.value.id, addressIndex: _subaddress.value.id);
|
||||
|
||||
@override
|
||||
Future<String> getSeed() async => monero_wallet.getSeed();
|
||||
|
||||
@override
|
||||
Future<String> getFullBalance() async => moneroAmountToString(
|
||||
amount: monero_wallet.getFullBalance(accountIndex: _account.value.id));
|
||||
|
||||
@override
|
||||
Future<String> getUnlockedBalance() async => moneroAmountToString(
|
||||
amount:
|
||||
monero_wallet.getUnlockedBalance(accountIndex: _account.value.id));
|
||||
|
||||
@override
|
||||
Future<int> getCurrentHeight() async => monero_wallet.getCurrentHeight();
|
||||
|
||||
@override
|
||||
Future<int> getNodeHeight() async => monero_wallet.getNodeHeight();
|
||||
|
||||
@override
|
||||
Future<bool> isConnected() async => monero_wallet.isConnected();
|
||||
|
||||
@override
|
||||
Future<Map<String, String>> getKeys() async => {
|
||||
'publicViewKey': monero_wallet.getPublicViewKey(),
|
||||
'privateViewKey': monero_wallet.getSecretViewKey(),
|
||||
'publicSpendKey': monero_wallet.getPublicSpendKey(),
|
||||
'privateSpendKey': monero_wallet.getSecretSpendKey()
|
||||
};
|
||||
|
||||
@override
|
||||
TransactionHistory getHistory() {
|
||||
if (_cachedTransactionHistory == null) {
|
||||
_cachedTransactionHistory = MoneroTransactionHistory();
|
||||
}
|
||||
|
||||
return _cachedTransactionHistory;
|
||||
}
|
||||
|
||||
SubaddressList getSubaddress() {
|
||||
if (_cachedSubaddressList == null) {
|
||||
_cachedSubaddressList = SubaddressList();
|
||||
}
|
||||
|
||||
return _cachedSubaddressList;
|
||||
}
|
||||
|
||||
AccountList getAccountList() {
|
||||
if (_cachedAccountList == null) {
|
||||
_cachedAccountList = AccountList();
|
||||
}
|
||||
|
||||
return _cachedAccountList;
|
||||
}
|
||||
|
||||
@override
|
||||
Future close() async {
|
||||
// monero_wallet.closeListeners();
|
||||
monero_wallet.closeCurrentWallet();
|
||||
await _name.close();
|
||||
await _address.close();
|
||||
await _subaddress.close();
|
||||
}
|
||||
|
||||
@override
|
||||
Future connectToNode(
|
||||
{Node node, bool useSSL = false, bool isLightWallet = false}) async {
|
||||
try {
|
||||
_syncStatus.value = ConnectingSyncStatus();
|
||||
await monero_wallet.setupNode(
|
||||
address: node.uri,
|
||||
login: node.login,
|
||||
password: node.password,
|
||||
useSSL: useSSL,
|
||||
isLightWallet: isLightWallet);
|
||||
_syncStatus.value = ConnectedSyncStatus();
|
||||
} catch (e) {
|
||||
_syncStatus.value = FailedSyncStatus();
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future startSync() async {
|
||||
try {
|
||||
_syncStatus.value = StartingSyncStatus();
|
||||
monero_wallet.startRefresh();
|
||||
} on PlatformException catch (e) {
|
||||
_syncStatus.value = FailedSyncStatus();
|
||||
print(e);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future askForSave() async {
|
||||
final diff = DateTime.now().millisecondsSinceEpoch - _lastSaveTime;
|
||||
|
||||
if (_lastSaveTime != 0 && diff < 120000) {
|
||||
return;
|
||||
}
|
||||
|
||||
await store();
|
||||
}
|
||||
|
||||
Future<int> getNodeHeightOrUpdate(int baseHeight) async {
|
||||
if (_cachedBlockchainHeight < baseHeight) {
|
||||
_cachedBlockchainHeight = await getNodeHeight();
|
||||
}
|
||||
|
||||
return _cachedBlockchainHeight;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<PendingTransaction> createTransaction(
|
||||
TransactionCreationCredentials credentials) async {
|
||||
final _credentials = credentials as MoneroTransactionCreationCredentials;
|
||||
final transactionDescription = await transaction_history.createTransaction(
|
||||
address: _credentials.address,
|
||||
paymentId: _credentials.paymentId,
|
||||
amount: _credentials.amount,
|
||||
priorityRaw: _credentials.priority.serialize(),
|
||||
accountIndex: _account.value.id);
|
||||
|
||||
return PendingTransaction.fromTransactionDescription(
|
||||
transactionDescription);
|
||||
}
|
||||
|
||||
@override
|
||||
Future rescan({int restoreHeight = 0}) async {
|
||||
_syncStatus.value = StartingSyncStatus();
|
||||
setRefreshFromBlockHeight(height: restoreHeight);
|
||||
monero_wallet.rescanBlockchainAsync();
|
||||
_syncStatus.value = StartingSyncStatus();
|
||||
}
|
||||
|
||||
void setRecoveringFromSeed() =>
|
||||
monero_wallet.setRecoveringFromSeed(isRecovery: true);
|
||||
|
||||
void setRefreshFromBlockHeight({int height}) =>
|
||||
monero_wallet.setRefreshFromBlockHeight(height: height);
|
||||
|
||||
Future setAsRecovered() async {
|
||||
walletInfo.isRecovery = false;
|
||||
await walletInfo.save();
|
||||
}
|
||||
|
||||
Future askForUpdateBalance() async {
|
||||
final fullBalance = await getFullBalance();
|
||||
final unlockedBalance = await getUnlockedBalance();
|
||||
final needToChange = _onBalanceChange.value != null
|
||||
? _onBalanceChange.value.fullBalance != fullBalance ||
|
||||
_onBalanceChange.value.unlockedBalance != unlockedBalance
|
||||
: true;
|
||||
|
||||
if (!needToChange) {
|
||||
return;
|
||||
}
|
||||
|
||||
_onBalanceChange.add(MoneroBalance(
|
||||
fullBalance: fullBalance, unlockedBalance: unlockedBalance));
|
||||
}
|
||||
|
||||
Future askForUpdateTransactionHistory() async => await getHistory().update();
|
||||
|
||||
void changeCurrentSubaddress(Subaddress subaddress) =>
|
||||
_subaddress.value = subaddress;
|
||||
|
||||
void changeAccount(Account account) {
|
||||
_account.add(account);
|
||||
final subaddress = getSubaddress()..refresh(accountIndex: account.id);
|
||||
_subaddress.value = subaddress.getAll().first;
|
||||
}
|
||||
|
||||
Future store() async {
|
||||
if (_isSaving) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
_isSaving = true;
|
||||
await monero_wallet.store();
|
||||
_isSaving = false;
|
||||
} on PlatformException catch (e) {
|
||||
print(e);
|
||||
_isSaving = false;
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
void setListeners() => null;
|
||||
// monero_wallet.setListeners(
|
||||
// _onNewBlock, _onNeedToRefresh, _onNewTransaction);
|
||||
|
||||
// Future _onNewBlock(int height) async {
|
||||
// try {
|
||||
// final nodeHeight = await getNodeHeightOrUpdate(height);
|
||||
//
|
||||
// if (isRecovery && _refreshHeight <= 0) {
|
||||
// _refreshHeight = height;
|
||||
// }
|
||||
//
|
||||
// if (isRecovery &&
|
||||
// (_lastSyncHeight == 0 ||
|
||||
// (height - _lastSyncHeight) > moneroBlockSize)) {
|
||||
// _lastSyncHeight = height;
|
||||
// await askForUpdateBalance();
|
||||
// await askForUpdateTransactionHistory();
|
||||
// }
|
||||
//
|
||||
// if (height > 0 && ((nodeHeight - height) < moneroBlockSize)) {
|
||||
// _syncStatus.add(SyncedSyncStatus());
|
||||
// } else {
|
||||
// _syncStatus.add(SyncingSyncStatus(height, nodeHeight, _refreshHeight));
|
||||
// }
|
||||
// } catch (e) {
|
||||
// print(e);
|
||||
// }
|
||||
// }
|
||||
|
||||
// Future _onNeedToRefresh() async {
|
||||
// try {
|
||||
//
|
||||
//
|
||||
// if (_syncStatus.value is FailedSyncStatus) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// await askForUpdateBalance();
|
||||
//
|
||||
// _syncStatus.add(SyncedSyncStatus());
|
||||
//
|
||||
// if (isRecovery) {
|
||||
// await askForUpdateTransactionHistory();
|
||||
// }
|
||||
//
|
||||
//// if (isRecovery && (nodeHeight - currentHeight < moneroBlockSize)) {
|
||||
//// await setAsRecovered();
|
||||
//// }
|
||||
//
|
||||
// final now = DateTime.now().millisecondsSinceEpoch;
|
||||
// final diff = now - _lastRefreshTime;
|
||||
//
|
||||
// if (diff >= 0 && diff < 60000) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// await store();
|
||||
// _lastRefreshTime = now;
|
||||
// } catch (e) {
|
||||
// print(e);
|
||||
// }
|
||||
// }
|
||||
|
||||
// Future _onNewTransaction() async {
|
||||
// await askForUpdateBalance();
|
||||
// await askForUpdateTransactionHistory();
|
||||
// }
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'package:cake_wallet/src/domain/common/pathForWallet.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:cw_monero/wallet_manager.dart' as monero_wallet_manager;
|
||||
import 'package:cake_wallet/src/domain/common/wallet_info.dart';
|
||||
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
|
||||
import 'package:cake_wallet/src/domain/common/wallets_manager.dart';
|
||||
import 'package:cake_wallet/src/domain/common/wallet.dart';
|
||||
import 'package:cake_wallet/src/domain/monero/monero_wallet.dart';
|
||||
import 'package:cake_wallet/src/domain/common/wallet_description.dart';
|
||||
|
||||
class MoneroWalletsManager extends WalletsManager {
|
||||
MoneroWalletsManager({@required this.walletInfoSource});
|
||||
|
||||
static const type = WalletType.monero;
|
||||
|
||||
Box<WalletInfo> walletInfoSource;
|
||||
|
||||
@override
|
||||
Future<Wallet> create(String name, String password, String language) async {
|
||||
try {
|
||||
const isRecovery = false;
|
||||
final path = await pathForWallet(name: name, type: WalletType.monero);
|
||||
|
||||
await monero_wallet_manager.createWallet(
|
||||
path: path, password: password, language: language);
|
||||
|
||||
final wallet = await MoneroWallet.createdWallet(
|
||||
walletInfoSource: walletInfoSource,
|
||||
name: name,
|
||||
isRecovery: isRecovery);
|
||||
await wallet.updateInfo();
|
||||
|
||||
return wallet;
|
||||
} catch (e) {
|
||||
print('MoneroWalletsManager Error: $e');
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Wallet> restoreFromSeed(
|
||||
String name, String password, String seed, int restoreHeight) async {
|
||||
try {
|
||||
const isRecovery = true;
|
||||
final path = await pathForWallet(name: name, type: WalletType.monero);
|
||||
|
||||
await monero_wallet_manager.restoreFromSeed(
|
||||
path: path,
|
||||
password: password,
|
||||
seed: seed,
|
||||
restoreHeight: restoreHeight);
|
||||
|
||||
final wallet = await MoneroWallet.createdWallet(
|
||||
walletInfoSource: walletInfoSource,
|
||||
name: name,
|
||||
isRecovery: isRecovery,
|
||||
restoreHeight: restoreHeight);
|
||||
await wallet.updateInfo();
|
||||
|
||||
return wallet;
|
||||
} catch (e) {
|
||||
print('MoneroWalletsManager Error: $e');
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Wallet> restoreFromKeys(
|
||||
String name,
|
||||
String password,
|
||||
String language,
|
||||
int restoreHeight,
|
||||
String address,
|
||||
String viewKey,
|
||||
String spendKey) async {
|
||||
try {
|
||||
const isRecovery = true;
|
||||
final path = await pathForWallet(name: name, type: WalletType.monero);
|
||||
|
||||
await monero_wallet_manager.restoreFromKeys(
|
||||
path: path,
|
||||
password: password,
|
||||
language: language,
|
||||
restoreHeight: restoreHeight,
|
||||
address: address,
|
||||
viewKey: viewKey,
|
||||
spendKey: spendKey);
|
||||
|
||||
final wallet = await MoneroWallet.createdWallet(
|
||||
walletInfoSource: walletInfoSource,
|
||||
name: name,
|
||||
isRecovery: isRecovery,
|
||||
restoreHeight: restoreHeight);
|
||||
await wallet.updateInfo();
|
||||
|
||||
return wallet;
|
||||
} catch (e) {
|
||||
print('MoneroWalletsManager Error: $e');
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Wallet> openWallet(String name, String password) async {
|
||||
try {
|
||||
final path = await pathForWallet(name: name, type: WalletType.monero);
|
||||
monero_wallet_manager.openWallet(path: path, password: password);
|
||||
final wallet = await MoneroWallet.load(walletInfoSource, name, type);
|
||||
await wallet.updateInfo();
|
||||
|
||||
return wallet;
|
||||
} catch (e) {
|
||||
print('MoneroWalletsManager Error: $e');
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> isWalletExit(String name) async {
|
||||
try {
|
||||
final path = await pathForWallet(name: name, type: WalletType.monero);
|
||||
return monero_wallet_manager.isWalletExist(path: path);
|
||||
} catch (e) {
|
||||
print('MoneroWalletsManager Error: $e');
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future remove(WalletDescription wallet) async {
|
||||
final dir = await getApplicationDocumentsDirectory();
|
||||
final root = dir.path.replaceAll('app_flutter', 'files');
|
||||
final walletFilePath = root + '/cw_monero/' + wallet.name;
|
||||
final keyPath = walletFilePath + '.keys';
|
||||
final addressFilePath = walletFilePath + '.address.txt';
|
||||
final walletFile = File(walletFilePath);
|
||||
final keyFile = File(keyPath);
|
||||
final addressFile = File(addressFilePath);
|
||||
|
||||
if (await walletFile.exists()) {
|
||||
await walletFile.delete();
|
||||
}
|
||||
|
||||
if (await keyFile.exists()) {
|
||||
await keyFile.delete();
|
||||
}
|
||||
|
||||
if (await addressFile.exists()) {
|
||||
await addressFile.delete();
|
||||
}
|
||||
|
||||
final id =
|
||||
walletTypeToString(wallet.type).toLowerCase() + '_' + wallet.name;
|
||||
final info = walletInfoSource.values
|
||||
.firstWhere((info) => info.id == id, orElse: () => null);
|
||||
|
||||
await info?.delete();
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
import 'package:cw_monero/structs/subaddress_row.dart';
|
||||
|
||||
class Subaddress {
|
||||
Subaddress({this.id, this.address, this.label});
|
||||
|
||||
Subaddress.fromMap(Map map)
|
||||
: this.id = map['id'] == null ? 0 : int.parse(map['id'] as String),
|
||||
this.address = (map['address'] ?? '') as String,
|
||||
this.label = (map['label'] ?? '') as String;
|
||||
|
||||
Subaddress.fromRow(SubaddressRow row)
|
||||
: this.id = row.getId(),
|
||||
this.address = row.getAddress(),
|
||||
this.label = row.getLabel();
|
||||
|
||||
final int id;
|
||||
final String address;
|
||||
final String label;
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:rxdart/rxdart.dart';
|
||||
import 'package:cw_monero/subaddress_list.dart' as subaddress_list;
|
||||
import 'package:cake_wallet/src/domain/monero/subaddress.dart';
|
||||
|
||||
class SubaddressList {
|
||||
SubaddressList() {
|
||||
_isRefreshing = false;
|
||||
_isUpdating = false;
|
||||
_subaddress = BehaviorSubject<List<Subaddress>>();
|
||||
}
|
||||
|
||||
Observable<List<Subaddress>> get subaddresses => _subaddress.stream;
|
||||
|
||||
BehaviorSubject<List<Subaddress>> _subaddress;
|
||||
bool _isRefreshing;
|
||||
bool _isUpdating;
|
||||
|
||||
void update({int accountIndex}) {
|
||||
if (_isUpdating) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
_isUpdating = true;
|
||||
refresh(accountIndex: accountIndex);
|
||||
_subaddress.add(getAll());
|
||||
_isUpdating = false;
|
||||
} catch (e) {
|
||||
_isUpdating = false;
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
List<Subaddress> getAll() {
|
||||
return subaddress_list
|
||||
.getAllSubaddresses()
|
||||
.map((subaddressRow) => Subaddress.fromRow(subaddressRow))
|
||||
.toList();
|
||||
}
|
||||
|
||||
Future addSubaddress({int accountIndex, String label}) async {
|
||||
await subaddress_list.addSubaddress(
|
||||
accountIndex: accountIndex, label: label);
|
||||
await update(accountIndex: accountIndex);
|
||||
}
|
||||
|
||||
Future setLabelSubaddress(
|
||||
{int accountIndex, int addressIndex, String label}) async {
|
||||
await subaddress_list.setLabelForSubaddress(
|
||||
accountIndex: accountIndex, addressIndex: addressIndex, label: label);
|
||||
await update(accountIndex: accountIndex);
|
||||
}
|
||||
|
||||
void refresh({int accountIndex}) {
|
||||
if (_isRefreshing) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
_isRefreshing = true;
|
||||
subaddress_list.refreshSubaddresses(accountIndex: accountIndex);
|
||||
_isRefreshing = false;
|
||||
} on PlatformException catch (e) {
|
||||
_isRefreshing = false;
|
||||
print(e);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
part 'transaction_description.g.dart';
|
||||
|
||||
@HiveType(typeId: 2)
|
||||
class TransactionDescription extends HiveObject {
|
||||
TransactionDescription({this.id, this.recipientAddress});
|
||||
|
||||
static const boxName = 'TransactionDescriptions';
|
||||
|
||||
@HiveField(0)
|
||||
String id;
|
||||
|
||||
@HiveField(1)
|
||||
String recipientAddress;
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import 'dart:async';
|
||||
import 'package:cake_wallet/src/domain/common/fiat_currency.dart';
|
||||
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
|
||||
import 'package:cake_wallet/src/domain/common/fetch_price.dart';
|
||||
|
||||
class FiatConvertationService {
|
||||
Future<double> getPrice({CryptoCurrency crypto, FiatCurrency fiat}) async {
|
||||
return await fetchPriceFor(crypto: crypto, fiat: fiat);
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:cake_wallet/src/domain/common/secret_store_key.dart';
|
||||
import 'package:cake_wallet/src/domain/common/encrypt.dart';
|
||||
|
||||
class UserService {
|
||||
UserService({this.sharedPreferences, this.secureStorage});
|
||||
|
||||
final FlutterSecureStorage secureStorage;
|
||||
final SharedPreferences sharedPreferences;
|
||||
|
||||
Future setPassword(String password) async {
|
||||
final key = generateStoreKeyFor(key: SecretStoreKey.pinCodePassword);
|
||||
|
||||
try {
|
||||
final encodedPassord = encodedPinCode(pin: password);
|
||||
|
||||
await secureStorage.write(key: key, value: encodedPassord);
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> canAuthenticate() async {
|
||||
final key = generateStoreKeyFor(key: SecretStoreKey.pinCodePassword);
|
||||
final sharedPreferences = await SharedPreferences.getInstance();
|
||||
final walletName = sharedPreferences.getString("current_wallet_name") ?? "";
|
||||
var password = '';
|
||||
|
||||
try {
|
||||
password = await secureStorage.read(key: key);
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
|
||||
return walletName.isNotEmpty && password.isNotEmpty;
|
||||
}
|
||||
|
||||
Future<bool> authenticate(String pin) async {
|
||||
final key = generateStoreKeyFor(key: SecretStoreKey.pinCodePassword);
|
||||
final encodedPin = await secureStorage.read(key: key);
|
||||
final decodedPin = decodedPinCode(pin: encodedPin);
|
||||
|
||||
return decodedPin == pin;
|
||||
}
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
import 'dart:async';
|
||||
import 'package:cake_wallet/bitcoin/key.dart';
|
||||
import 'package:cake_wallet/src/domain/common/wallet_info.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:cake_wallet/src/domain/common/encrypt.dart';
|
||||
import 'package:cake_wallet/src/domain/common/wallet.dart';
|
||||
import 'package:cake_wallet/src/domain/common/wallet_description.dart';
|
||||
import 'package:cake_wallet/src/domain/common/wallets_manager.dart';
|
||||
import 'package:cake_wallet/src/domain/common/secret_store_key.dart';
|
||||
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
|
||||
import 'package:cake_wallet/src/domain/monero/monero_wallets_manager.dart';
|
||||
import 'package:cake_wallet/src/domain/services/wallet_service.dart';
|
||||
|
||||
class WalletIsExistException implements Exception {
|
||||
WalletIsExistException(this.name);
|
||||
|
||||
String name;
|
||||
|
||||
@override
|
||||
String toString() => "Wallet with name $name is already exist!";
|
||||
}
|
||||
|
||||
class WalletListService {
|
||||
WalletListService(
|
||||
{this.secureStorage,
|
||||
this.walletInfoSource,
|
||||
this.walletsManager,
|
||||
@required this.walletService,
|
||||
@required this.sharedPreferences})
|
||||
: _type = WalletType.monero;
|
||||
|
||||
final FlutterSecureStorage secureStorage;
|
||||
final WalletService walletService;
|
||||
final Box<WalletInfo> walletInfoSource;
|
||||
final SharedPreferences sharedPreferences;
|
||||
WalletsManager walletsManager;
|
||||
WalletType _type;
|
||||
|
||||
Future<List<WalletDescription>> getAll() async => walletInfoSource.values
|
||||
.map((info) => WalletDescription(name: info.name, type: info.type))
|
||||
.toList();
|
||||
|
||||
Future create(String name, String language) async {
|
||||
if (await walletsManager.isWalletExit(name)) {
|
||||
throw WalletIsExistException(name);
|
||||
}
|
||||
|
||||
if (walletService.currentWallet != null) {
|
||||
await walletService.close();
|
||||
}
|
||||
|
||||
final password = _generatePassword();
|
||||
await saveWalletPassword(password: password, walletName: name);
|
||||
|
||||
final wallet = await walletsManager.create(name, password, language);
|
||||
|
||||
await onWalletChange(wallet);
|
||||
}
|
||||
|
||||
Future restoreFromSeed(String name, String seed, int restoreHeight) async {
|
||||
if (await walletsManager.isWalletExit(name)) {
|
||||
throw WalletIsExistException(name);
|
||||
}
|
||||
|
||||
if (walletService.currentWallet != null) {
|
||||
await walletService.close();
|
||||
}
|
||||
|
||||
final password = _generatePassword();
|
||||
await saveWalletPassword(password: password, walletName: name);
|
||||
|
||||
final wallet = await walletsManager.restoreFromSeed(
|
||||
name, password, seed, restoreHeight);
|
||||
|
||||
await onWalletChange(wallet);
|
||||
}
|
||||
|
||||
Future restoreFromKeys(String name, String language, int restoreHeight,
|
||||
String address, String viewKey, String spendKey) async {
|
||||
if (await walletsManager.isWalletExit(name)) {
|
||||
throw WalletIsExistException(name);
|
||||
}
|
||||
|
||||
if (walletService.currentWallet != null) {
|
||||
await walletService.close();
|
||||
}
|
||||
|
||||
final password = _generatePassword();
|
||||
await saveWalletPassword(password: password, walletName: name);
|
||||
|
||||
final wallet = await walletsManager.restoreFromKeys(
|
||||
name, password, language, restoreHeight, address, viewKey, spendKey);
|
||||
|
||||
await onWalletChange(wallet);
|
||||
}
|
||||
|
||||
Future openWallet(String name) async {
|
||||
if (walletService.currentWallet != null) {
|
||||
await walletService.close();
|
||||
}
|
||||
|
||||
final password = await getWalletPassword(walletName: name);
|
||||
final wallet = await walletsManager.openWallet(name, password);
|
||||
|
||||
await onWalletChange(wallet);
|
||||
}
|
||||
|
||||
Future changeWalletManger({WalletType walletType}) async {
|
||||
_type = walletType;
|
||||
|
||||
switch (walletType) {
|
||||
case WalletType.monero:
|
||||
walletsManager =
|
||||
MoneroWalletsManager(walletInfoSource: walletInfoSource);
|
||||
break;
|
||||
case WalletType.bitcoin:
|
||||
// walletsManager = BitcoinWalletManager();
|
||||
break;
|
||||
case WalletType.none:
|
||||
walletsManager = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Future onWalletChange(Wallet wallet) async {
|
||||
walletService.currentWallet = wallet;
|
||||
final walletName = await wallet.getName();
|
||||
print('walletName $walletName ');
|
||||
await sharedPreferences.setString('current_wallet_name', walletName);
|
||||
}
|
||||
|
||||
Future remove(WalletDescription wallet) async =>
|
||||
await walletsManager.remove(wallet);
|
||||
|
||||
Future<String> getWalletPassword({String walletName}) async {
|
||||
final key = generateStoreKeyFor(
|
||||
key: SecretStoreKey.moneroWalletPassword, walletName: walletName);
|
||||
final encodedPassword = await secureStorage.read(key: key);
|
||||
|
||||
return decodeWalletPassword(password: encodedPassword);
|
||||
}
|
||||
|
||||
Future saveWalletPassword({String walletName, String password}) async {
|
||||
final key = generateStoreKeyFor(
|
||||
key: SecretStoreKey.moneroWalletPassword, walletName: walletName);
|
||||
final encodedPassword = encodeWalletPassword(password: password);
|
||||
|
||||
await secureStorage.write(key: key, value: encodedPassword);
|
||||
}
|
||||
|
||||
String _generatePassword() {
|
||||
switch (_type) {
|
||||
case WalletType.bitcoin:
|
||||
return generateKey();
|
||||
default:
|
||||
return Uuid().v4();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
import 'package:rxdart/rxdart.dart';
|
||||
import 'package:cake_wallet/src/domain/common/balance.dart';
|
||||
import 'package:cake_wallet/src/domain/common/wallet_description.dart';
|
||||
import 'package:cake_wallet/src/domain/common/wallet.dart';
|
||||
import 'package:cake_wallet/src/domain/common/transaction_history.dart';
|
||||
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
|
||||
import 'package:cake_wallet/src/domain/common/sync_status.dart';
|
||||
import 'package:cake_wallet/src/domain/common/transaction_creation_credentials.dart';
|
||||
import 'package:cake_wallet/src/domain/common/pending_transaction.dart';
|
||||
import 'package:cake_wallet/src/domain/common/node.dart';
|
||||
|
||||
class WalletService extends Wallet {
|
||||
WalletService() {
|
||||
_currentWallet = null;
|
||||
walletType = WalletType.none;
|
||||
_syncStatus = BehaviorSubject<SyncStatus>();
|
||||
_onBalanceChange = BehaviorSubject<Balance>();
|
||||
_onWalletChanged = BehaviorSubject<Wallet>();
|
||||
}
|
||||
|
||||
@override
|
||||
Observable<Balance> get onBalanceChange => _onBalanceChange.stream;
|
||||
|
||||
@override
|
||||
Observable<SyncStatus> get syncStatus => _syncStatus.stream;
|
||||
|
||||
@override
|
||||
Observable<String> get onAddressChange => _currentWallet.onAddressChange;
|
||||
|
||||
@override
|
||||
Observable<String> get onNameChange => _currentWallet.onNameChange;
|
||||
|
||||
@override
|
||||
String get address => _currentWallet.address;
|
||||
|
||||
@override
|
||||
String get name => _currentWallet.name;
|
||||
|
||||
@override
|
||||
WalletType get walletType => _currentWallet.walletType;
|
||||
|
||||
Observable<Wallet> get onWalletChange => _onWalletChanged.stream;
|
||||
|
||||
SyncStatus get syncStatusValue => _syncStatus.value;
|
||||
|
||||
Wallet get currentWallet => _currentWallet;
|
||||
|
||||
set currentWallet(Wallet wallet) {
|
||||
_currentWallet = wallet;
|
||||
|
||||
if (wallet == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
_currentWallet.onBalanceChange
|
||||
.listen((wallet) => _onBalanceChange.add(wallet));
|
||||
_currentWallet.syncStatus.listen((status) => _syncStatus.add(status));
|
||||
_onWalletChanged.add(wallet);
|
||||
|
||||
final type = wallet.getType();
|
||||
wallet.getName().then(
|
||||
(name) => description = WalletDescription(name: name, type: type));
|
||||
}
|
||||
|
||||
BehaviorSubject<Wallet> _onWalletChanged;
|
||||
BehaviorSubject<Balance> _onBalanceChange;
|
||||
BehaviorSubject<SyncStatus> _syncStatus;
|
||||
Wallet _currentWallet;
|
||||
|
||||
WalletDescription description;
|
||||
|
||||
@override
|
||||
WalletType getType() => _currentWallet.getType();
|
||||
|
||||
@override
|
||||
Future<String> getFilename() => _currentWallet.getFilename();
|
||||
|
||||
@override
|
||||
Future<String> getName() => _currentWallet.getName();
|
||||
|
||||
@override
|
||||
Future<String> getAddress() => _currentWallet.getAddress();
|
||||
|
||||
@override
|
||||
Future<String> getSeed() => _currentWallet.getSeed();
|
||||
|
||||
@override
|
||||
Future<Map<String, String>> getKeys() => _currentWallet.getKeys();
|
||||
|
||||
@override
|
||||
Future<String> getFullBalance() => _currentWallet.getFullBalance();
|
||||
|
||||
@override
|
||||
Future<String> getUnlockedBalance() => _currentWallet.getUnlockedBalance();
|
||||
|
||||
@override
|
||||
Future<int> getCurrentHeight() => _currentWallet.getCurrentHeight();
|
||||
|
||||
@override
|
||||
Future<int> getNodeHeight() => _currentWallet.getNodeHeight();
|
||||
|
||||
@override
|
||||
Future<bool> isConnected() => _currentWallet.isConnected();
|
||||
|
||||
@override
|
||||
Future close() => _currentWallet.close();
|
||||
|
||||
@override
|
||||
Future connectToNode(
|
||||
{Node node, bool useSSL = false, bool isLightWallet = false}) =>
|
||||
_currentWallet.connectToNode(
|
||||
node: node, useSSL: useSSL, isLightWallet: isLightWallet);
|
||||
|
||||
@override
|
||||
Future startSync() => _currentWallet.startSync();
|
||||
|
||||
@override
|
||||
TransactionHistory getHistory() => _currentWallet.getHistory();
|
||||
|
||||
@override
|
||||
Future<PendingTransaction> createTransaction(
|
||||
TransactionCreationCredentials credentials) =>
|
||||
_currentWallet.createTransaction(credentials);
|
||||
|
||||
@override
|
||||
Future updateInfo() async => _currentWallet.updateInfo();
|
||||
|
||||
@override
|
||||
Future rescan({int restoreHeight = 0}) async =>
|
||||
_currentWallet.rescan(restoreHeight: restoreHeight);
|
||||
}
|
||||
@@ -1,98 +1,98 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/src/domain/common/node.dart';
|
||||
import 'package:cake_wallet/src/domain/common/sync_status.dart';
|
||||
import 'package:cake_wallet/src/domain/services/wallet_service.dart';
|
||||
import 'package:cake_wallet/src/start_updating_price.dart';
|
||||
import 'package:cake_wallet/src/stores/sync/sync_store.dart';
|
||||
import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
|
||||
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
|
||||
import 'package:cake_wallet/src/stores/price/price_store.dart';
|
||||
import 'package:cake_wallet/src/stores/authentication/authentication_store.dart';
|
||||
import 'package:cake_wallet/src/stores/login/login_store.dart';
|
||||
|
||||
Timer _reconnectionTimer;
|
||||
ReactionDisposer _connectToNodeDisposer;
|
||||
ReactionDisposer _onSyncStatusChangeDisposer;
|
||||
ReactionDisposer _onCurrentWalletChangeDisposer;
|
||||
|
||||
void setReactions(
|
||||
{@required SettingsStore settingsStore,
|
||||
@required PriceStore priceStore,
|
||||
@required SyncStore syncStore,
|
||||
@required WalletStore walletStore,
|
||||
@required WalletService walletService,
|
||||
// @required AuthenticationStore authenticationStore,
|
||||
@required LoginStore loginStore}) {
|
||||
connectToNode(settingsStore: settingsStore, walletStore: walletStore);
|
||||
onSyncStatusChange(
|
||||
syncStore: syncStore,
|
||||
walletStore: walletStore,
|
||||
settingsStore: settingsStore);
|
||||
onCurrentWalletChange(
|
||||
walletStore: walletStore,
|
||||
settingsStore: settingsStore,
|
||||
priceStore: priceStore);
|
||||
autorun((_) async {
|
||||
// if (authenticationStore.state == AuthenticationState.allowed) {
|
||||
// await loginStore.loadCurrentWallet();
|
||||
// authenticationStore.state = AuthenticationState.readyToLogin;
|
||||
// }
|
||||
});
|
||||
}
|
||||
|
||||
void connectToNode({SettingsStore settingsStore, WalletStore walletStore}) {
|
||||
_connectToNodeDisposer?.call();
|
||||
|
||||
_connectToNodeDisposer = reaction((_) => settingsStore.node,
|
||||
(Node node) async => await walletStore.connectToNode(node: node));
|
||||
}
|
||||
|
||||
void onCurrentWalletChange(
|
||||
{WalletStore walletStore,
|
||||
SettingsStore settingsStore,
|
||||
PriceStore priceStore}) {
|
||||
_onCurrentWalletChangeDisposer?.call();
|
||||
|
||||
reaction((_) => walletStore.name, (String _) {
|
||||
walletStore.connectToNode(node: settingsStore.node);
|
||||
startUpdatingPrice(settingsStore: settingsStore, priceStore: priceStore);
|
||||
});
|
||||
}
|
||||
|
||||
void onSyncStatusChange(
|
||||
{SyncStore syncStore,
|
||||
WalletStore walletStore,
|
||||
SettingsStore settingsStore}) {
|
||||
// _onSyncStatusChangeDisposer?.call();
|
||||
|
||||
// reaction((_) => syncStore.status, (SyncStatus status) async {
|
||||
// if (status is ConnectedSyncStatus) {
|
||||
// await walletStore.startSync();
|
||||
// }
|
||||
|
||||
// // Reconnect to the node if the app is not started sync after 30 seconds
|
||||
// if (status is StartingSyncStatus) {
|
||||
// startReconnectionObserver(syncStore: syncStore, walletStore: walletStore);
|
||||
// }
|
||||
// });
|
||||
}
|
||||
|
||||
void startReconnectionObserver({SyncStore syncStore, WalletStore walletStore}) {
|
||||
if (_reconnectionTimer != null) {
|
||||
_reconnectionTimer.cancel();
|
||||
}
|
||||
|
||||
_reconnectionTimer = Timer.periodic(Duration(minutes: 1), (_) async {
|
||||
try {
|
||||
final isConnected = await walletStore.isConnected();
|
||||
|
||||
if (!isConnected) {
|
||||
await walletStore.reconnect();
|
||||
}
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
// import 'dart:async';
|
||||
// import 'package:flutter/foundation.dart';
|
||||
// import 'package:mobx/mobx.dart';
|
||||
// import 'package:cake_wallet/entities/node.dart';
|
||||
// import 'package:cake_wallet/entities/sync_status.dart';
|
||||
// import 'package:cake_wallet/src/domain/services/wallet_service.dart';
|
||||
// import 'package:cake_wallet/src/start_updating_price.dart';
|
||||
// import 'package:cake_wallet/src/stores/sync/sync_store.dart';
|
||||
// import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
|
||||
// import 'package:cake_wallet/src/stores/settings/settings_store.dart';
|
||||
// import 'package:cake_wallet/src/stores/price/price_store.dart';
|
||||
// import 'package:cake_wallet/src/stores/authentication/authentication_store.dart';
|
||||
// import 'package:cake_wallet/src/stores/login/login_store.dart';
|
||||
//
|
||||
// Timer _reconnectionTimer;
|
||||
// ReactionDisposer _connectToNodeDisposer;
|
||||
// ReactionDisposer _onSyncStatusChangeDisposer;
|
||||
// ReactionDisposer _onCurrentWalletChangeDisposer;
|
||||
//
|
||||
// void setReactions(
|
||||
// {@required SettingsStore settingsStore,
|
||||
// @required PriceStore priceStore,
|
||||
// @required SyncStore syncStore,
|
||||
// @required WalletStore walletStore,
|
||||
// @required WalletService walletService,
|
||||
// // @required AuthenticationStore authenticationStore,
|
||||
// @required LoginStore loginStore}) {
|
||||
// connectToNode(settingsStore: settingsStore, walletStore: walletStore);
|
||||
// onSyncStatusChange(
|
||||
// syncStore: syncStore,
|
||||
// walletStore: walletStore,
|
||||
// settingsStore: settingsStore);
|
||||
// onCurrentWalletChange(
|
||||
// walletStore: walletStore,
|
||||
// settingsStore: settingsStore,
|
||||
// priceStore: priceStore);
|
||||
// autorun((_) async {
|
||||
// // if (authenticationStore.state == AuthenticationState.allowed) {
|
||||
// // await loginStore.loadCurrentWallet();
|
||||
// // authenticationStore.state = AuthenticationState.readyToLogin;
|
||||
// // }
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// void connectToNode({SettingsStore settingsStore, WalletStore walletStore}) {
|
||||
// _connectToNodeDisposer?.call();
|
||||
//
|
||||
// _connectToNodeDisposer = reaction((_) => settingsStore.node,
|
||||
// (Node node) async => await walletStore.connectToNode(node: node));
|
||||
// }
|
||||
//
|
||||
// void onCurrentWalletChange(
|
||||
// {WalletStore walletStore,
|
||||
// SettingsStore settingsStore,
|
||||
// PriceStore priceStore}) {
|
||||
// _onCurrentWalletChangeDisposer?.call();
|
||||
//
|
||||
// reaction((_) => walletStore.name, (String _) {
|
||||
// walletStore.connectToNode(node: settingsStore.node);
|
||||
// startUpdatingPrice(settingsStore: settingsStore, priceStore: priceStore);
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// void onSyncStatusChange(
|
||||
// {SyncStore syncStore,
|
||||
// WalletStore walletStore,
|
||||
// SettingsStore settingsStore}) {
|
||||
// // _onSyncStatusChangeDisposer?.call();
|
||||
//
|
||||
// // reaction((_) => syncStore.status, (SyncStatus status) async {
|
||||
// // if (status is ConnectedSyncStatus) {
|
||||
// // await walletStore.startSync();
|
||||
// // }
|
||||
//
|
||||
// // // Reconnect to the node if the app is not started sync after 30 seconds
|
||||
// // if (status is StartingSyncStatus) {
|
||||
// // startReconnectionObserver(syncStore: syncStore, walletStore: walletStore);
|
||||
// // }
|
||||
// // });
|
||||
// }
|
||||
//
|
||||
// void startReconnectionObserver({SyncStore syncStore, WalletStore walletStore}) {
|
||||
// if (_reconnectionTimer != null) {
|
||||
// _reconnectionTimer.cancel();
|
||||
// }
|
||||
//
|
||||
// _reconnectionTimer = Timer.periodic(Duration(minutes: 1), (_) async {
|
||||
// try {
|
||||
// final isConnected = await walletStore.isConnected();
|
||||
//
|
||||
// if (!isConnected) {
|
||||
// await walletStore.reconnect();
|
||||
// }
|
||||
// } catch (e) {
|
||||
// print(e);
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
@@ -5,20 +5,18 @@ import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/view_model/auth_state.dart';
|
||||
import 'package:cake_wallet/view_model/auth_view_model.dart';
|
||||
import 'package:cake_wallet/src/screens/pin_code/pin_code.dart';
|
||||
import 'package:cake_wallet/src/domain/common/biometric_auth.dart';
|
||||
import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart';
|
||||
import 'package:cake_wallet/entities/biometric_auth.dart';
|
||||
import 'package:cake_wallet/core/execution_state.dart';
|
||||
|
||||
typedef OnAuthenticationFinished = void Function(bool, AuthPageState);
|
||||
|
||||
class AuthPage extends StatefulWidget {
|
||||
AuthPage(
|
||||
{@required this.allowBiometricalAuthentication,
|
||||
this.onAuthenticationFinished,
|
||||
this.authViewModel,
|
||||
this.closable = true});
|
||||
AuthPage(this.authViewModel,
|
||||
{this.onAuthenticationFinished, this.closable = true});
|
||||
|
||||
final AuthViewModel authViewModel;
|
||||
final OnAuthenticationFinished onAuthenticationFinished;
|
||||
final bool allowBiometricalAuthentication;
|
||||
final bool closable;
|
||||
|
||||
@override
|
||||
@@ -35,8 +33,8 @@ class AuthPageState extends State<AuthPage> {
|
||||
@override
|
||||
void initState() {
|
||||
_reaction ??=
|
||||
reaction((_) => widget.authViewModel.state, (AuthState state) {
|
||||
if (state is AuthenticatedSuccessfully) {
|
||||
reaction((_) => widget.authViewModel.state, (ExecutionState state) {
|
||||
if (state is ExecutedSuccessfullyState) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (widget.onAuthenticationFinished != null) {
|
||||
widget.onAuthenticationFinished(true, this);
|
||||
@@ -51,7 +49,7 @@ class AuthPageState extends State<AuthPage> {
|
||||
});
|
||||
}
|
||||
|
||||
if (state is AuthenticationInProgress) {
|
||||
if (state is IsExecutingState) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_key.currentState.showSnackBar(
|
||||
SnackBar(
|
||||
@@ -62,7 +60,7 @@ class AuthPageState extends State<AuthPage> {
|
||||
});
|
||||
}
|
||||
|
||||
if (state is AuthenticationFailure) {
|
||||
if (state is FailureState) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_pinCodeKey.currentState.clear();
|
||||
_key.currentState.hideCurrentSnackBar();
|
||||
@@ -97,23 +95,10 @@ class AuthPageState extends State<AuthPage> {
|
||||
}
|
||||
});
|
||||
|
||||
if (widget.allowBiometricalAuthentication) {
|
||||
if (widget.authViewModel.isBiometricalAuthenticationAllowed) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
print('post');
|
||||
await Future<void>.delayed(Duration(milliseconds: 100));
|
||||
print('after timeout');
|
||||
final biometricAuth = BiometricAuth();
|
||||
final isAuth = await biometricAuth.isAuthenticated();
|
||||
|
||||
if (isAuth) {
|
||||
widget.authViewModel.biometricAuth();
|
||||
_key.currentState.showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(S.of(context).authenticated),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
);
|
||||
}
|
||||
await widget.authViewModel.biometricAuth();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -133,34 +118,28 @@ class AuthPageState extends State<AuthPage> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
print('start');
|
||||
|
||||
return Scaffold(
|
||||
key: _key,
|
||||
appBar: CupertinoNavigationBar(
|
||||
leading: widget.closable
|
||||
? SizedBox(
|
||||
height: 37,
|
||||
width: 20,
|
||||
child: ButtonTheme(
|
||||
minWidth: double.minPositive,
|
||||
child: FlatButton(
|
||||
highlightColor: Colors.transparent,
|
||||
splashColor: Colors.transparent,
|
||||
padding: EdgeInsets.all(0),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: _backArrowImageDarkTheme),
|
||||
),
|
||||
)
|
||||
: Container(),
|
||||
backgroundColor: Theme.of(context).backgroundColor,
|
||||
border: null,
|
||||
),
|
||||
leading: widget.closable
|
||||
? SizedBox(
|
||||
height: 37,
|
||||
width: 20,
|
||||
child: ButtonTheme(
|
||||
minWidth: double.minPositive,
|
||||
child: FlatButton(
|
||||
highlightColor: Colors.transparent,
|
||||
splashColor: Colors.transparent,
|
||||
padding: EdgeInsets.all(0),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: _backArrowImageDarkTheme),
|
||||
),
|
||||
)
|
||||
: Container(),
|
||||
backgroundColor: Theme.of(context).backgroundColor,
|
||||
border: null),
|
||||
resizeToAvoidBottomPadding: false,
|
||||
body: PinCode(
|
||||
(pin, _) => widget.authViewModel
|
||||
.auth(password: pin.fold('', (ac, val) => ac + '$val')),
|
||||
false,
|
||||
_pinCodeKey));
|
||||
body: PinCode((pin, _) => widget.authViewModel.auth(password: pin),
|
||||
(_) => null, widget.authViewModel.pinLength, false, _pinCodeKey));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:cake_wallet/src/domain/services/user_service.dart';
|
||||
import 'package:cake_wallet/src/domain/services/wallet_list_service.dart';
|
||||
import 'package:cake_wallet/src/domain/services/wallet_service.dart';
|
||||
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
|
||||
import 'package:cake_wallet/src/stores/auth/auth_store.dart';
|
||||
import 'package:cake_wallet/src/stores/authentication/authentication_store.dart';
|
||||
|
||||
Widget createLoginPage(
|
||||
{@required SharedPreferences sharedPreferences,
|
||||
@required UserService userService,
|
||||
@required WalletService walletService,
|
||||
@required WalletListService walletListService,
|
||||
@required AuthenticationStore authenticationStore}) =>
|
||||
null;
|
||||
// Provider(
|
||||
// create: (_) => AuthStore(
|
||||
// sharedPreferences: sharedPreferences,
|
||||
// userService: userService,
|
||||
// walletService: walletService),
|
||||
// child: AuthPage(
|
||||
// onAuthenticationFinished: (isAuthenticated, state) {
|
||||
// if (isAuthenticated) {
|
||||
// authenticationStore.loggedIn();
|
||||
// }
|
||||
// },
|
||||
// closable: false));
|
||||
@@ -1,24 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:cake_wallet/src/domain/services/user_service.dart';
|
||||
import 'package:cake_wallet/src/domain/services/wallet_service.dart';
|
||||
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
|
||||
import 'package:cake_wallet/src/stores/auth/auth_store.dart';
|
||||
|
||||
Widget createUnlockPage(
|
||||
{@required SharedPreferences sharedPreferences,
|
||||
@required UserService userService,
|
||||
@required WalletService walletService,
|
||||
@required Function(bool, AuthPageState) onAuthenticationFinished}) =>
|
||||
null;
|
||||
// WillPopScope(
|
||||
// onWillPop: () async => false,
|
||||
// child: Provider(
|
||||
// create: (_) => AuthStore(
|
||||
// sharedPreferences: sharedPreferences,
|
||||
// userService: userService,
|
||||
// walletService: walletService),
|
||||
// child: AuthPage(
|
||||
// onAuthenticationFinished: onAuthenticationFinished,
|
||||
// closable: false)));
|
||||
@@ -94,8 +94,8 @@ abstract class BasePage extends StatelessWidget {
|
||||
leading: leading(context),
|
||||
middle: middle(context),
|
||||
trailing: trailing(context),
|
||||
backgroundColor:
|
||||
_isDarkTheme ? backgroundDarkColor : backgroundLightColor);
|
||||
backgroundColor: Colors.transparent);
|
||||
// _isDarkTheme ? backgroundDarkColor : backgroundLightColor);
|
||||
|
||||
case AppBarStyle.withShadow:
|
||||
return NavBar.withShadow(
|
||||
@@ -131,7 +131,7 @@ abstract class BasePage extends StatelessWidget {
|
||||
resizeToAvoidBottomPadding: resizeToAvoidBottomPadding,
|
||||
endDrawer: endDrawer,
|
||||
appBar: appBar(context),
|
||||
body: body(context), //SafeArea(child: ),
|
||||
body: body(context),
|
||||
floatingActionButton: floatingActionButton(context));
|
||||
|
||||
return rootWrapper?.call(context, root) ?? root;
|
||||
|
||||
@@ -5,7 +5,7 @@ import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
|
||||
import 'package:cake_wallet/entities/crypto_currency.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||
import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
|
||||
|
||||
@@ -6,9 +6,9 @@ import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/core/address_validator.dart';
|
||||
import 'package:cake_wallet/core/contact_name_validator.dart';
|
||||
import 'package:cake_wallet/core/execution_state.dart';
|
||||
import 'package:cake_wallet/view_model/contact_list/contact_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/contact_list/contact_view_model_state.dart';
|
||||
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
|
||||
import 'package:cake_wallet/entities/crypto_currency.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
@@ -48,12 +48,12 @@ class ContactPage extends BasePage {
|
||||
final downArrow = Image.asset('assets/images/arrow_bottom_purple_icon.png',
|
||||
color: Theme.of(context).primaryTextTheme.overline.color, height: 8);
|
||||
|
||||
reaction((_) => contactViewModel.state, (ContactViewModelState state) {
|
||||
if (state is ContactCreationFailure) {
|
||||
reaction((_) => contactViewModel.state, (ExecutionState state) {
|
||||
if (state is FailureState) {
|
||||
_onContactSavingFailure(context, state.error);
|
||||
}
|
||||
|
||||
if (state is ContactSavingSuccessfully) {
|
||||
if (state is ExecutedSuccessfullyState) {
|
||||
_onContactSavedSuccessfully(context);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,31 +1,33 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/trade.dart';
|
||||
import 'package:cake_wallet/src/domain/monero/transaction_description.dart';
|
||||
import 'package:cake_wallet/src/domain/services/wallet_service.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/dashboard_page.dart';
|
||||
import 'package:cake_wallet/src/stores/action_list/action_list_store.dart';
|
||||
import 'package:cake_wallet/src/stores/action_list/trade_filter_store.dart';
|
||||
import 'package:cake_wallet/src/stores/action_list/transaction_filter_store.dart';
|
||||
import 'package:cake_wallet/src/stores/price/price_store.dart';
|
||||
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
|
||||
import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
|
||||
import 'package:cake_wallet/exchange/trade.dart';
|
||||
// import 'package:cake_wallet/monero/transaction_description.dart';
|
||||
// import 'package:cake_wallet/src/domain/services/wallet_service.dart';
|
||||
// import 'package:cake_wallet/src/screens/dashboard/dashboard_page.dart';
|
||||
// import 'package:cake_wallet/src/stores/action_list/action_list_store.dart';
|
||||
// import 'package:cake_wallet/src/stores/action_list/trade_filter_store.dart';
|
||||
// import 'package:cake_wallet/src/stores/action_list/transaction_filter_store.dart';
|
||||
// import 'package:cake_wallet/src/stores/price/price_store.dart';
|
||||
// import 'package:cake_wallet/src/stores/settings/settings_store.dart';
|
||||
// import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
|
||||
|
||||
Widget createDashboardPage(
|
||||
{@required WalletService walletService,
|
||||
@required PriceStore priceStore,
|
||||
@required Box<TransactionDescription> transactionDescriptions,
|
||||
@required SettingsStore settingsStore,
|
||||
@required Box<Trade> trades,
|
||||
@required WalletStore walletStore}) =>
|
||||
Provider(
|
||||
create: (_) => ActionListStore(
|
||||
walletService: walletService,
|
||||
settingsStore: settingsStore,
|
||||
priceStore: priceStore,
|
||||
tradesSource: trades,
|
||||
transactionFilterStore: TransactionFilterStore(),
|
||||
tradeFilterStore: TradeFilterStore(walletStore: walletStore),
|
||||
transactionDescriptions: transactionDescriptions),
|
||||
child: DashboardPage());
|
||||
// FIXME: Remove me.
|
||||
|
||||
// Widget createDashboardPage(
|
||||
// {@required WalletService walletService,
|
||||
// @required PriceStore priceStore,
|
||||
// @required Box<TransactionDescription> transactionDescriptions,
|
||||
// @required SettingsStore settingsStore,
|
||||
// @required Box<Trade> trades,
|
||||
// @required WalletStore walletStore}) =>
|
||||
// Provider(
|
||||
// create: (_) => ActionListStore(
|
||||
// walletService: walletService,
|
||||
// settingsStore: settingsStore,
|
||||
// priceStore: priceStore,
|
||||
// tradesSource: trades,
|
||||
// transactionFilterStore: TransactionFilterStore(),
|
||||
// tradeFilterStore: TradeFilterStore(walletStore: walletStore),
|
||||
// transactionDescriptions: transactionDescriptions),
|
||||
// child: DashboardPage());
|
||||
|
||||
@@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
|
||||
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||
|
||||
|
||||
@@ -1,26 +1,21 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
|
||||
import 'package:cake_wallet/utils/date_formatter.dart';
|
||||
|
||||
class DateSectionRaw extends StatelessWidget {
|
||||
DateSectionRaw({this.date});
|
||||
|
||||
static final nowDate = DateTime.now();
|
||||
final DateTime date;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final nowDate = DateTime.now();
|
||||
final diffDays = date.difference(nowDate).inDays;
|
||||
final isToday = nowDate.day == date.day &&
|
||||
nowDate.month == date.month &&
|
||||
nowDate.year == date.year;
|
||||
final settingsStore = Provider.of<SettingsStore>(context);
|
||||
final currentLanguage = settingsStore.languageCode;
|
||||
final dateSectionDateFormat = settingsStore.getCurrentDateFormat(
|
||||
formatUSA: "yyyy MMM d",
|
||||
formatDefault: "d MMM yyyy");
|
||||
final dateSectionDateFormat = DateFormatter.withCurrentLocal();
|
||||
var title = "";
|
||||
|
||||
if (isToday) {
|
||||
@@ -28,21 +23,19 @@ class DateSectionRaw extends StatelessWidget {
|
||||
} else if (diffDays == 0) {
|
||||
title = S.of(context).yesterday;
|
||||
} else if (diffDays > -7 && diffDays < 0) {
|
||||
final dateFormat = DateFormat.EEEE(currentLanguage);
|
||||
final dateFormat = DateFormat.EEEE();
|
||||
title = dateFormat.format(date);
|
||||
} else {
|
||||
title = dateSectionDateFormat.format(date);
|
||||
}
|
||||
|
||||
return Container(
|
||||
height: 35,
|
||||
alignment: Alignment.center,
|
||||
color: Colors.transparent,
|
||||
child: Text(title,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Theme.of(context).textTheme.overline.backgroundColor
|
||||
))
|
||||
);
|
||||
height: 35,
|
||||
alignment: Alignment.center,
|
||||
color: Colors.transparent,
|
||||
child: Text(title,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Theme.of(context).textTheme.overline.backgroundColor)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import 'dart:ui';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
|
||||
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
|
||||
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/wallet_menu.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:cake_wallet/src/domain/common/sync_status.dart';
|
||||
import 'package:cake_wallet/entities/sync_status.dart';
|
||||
|
||||
class SyncIndicator extends StatelessWidget {
|
||||
SyncIndicator({@required this.dashboardViewModel});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user