stack_wallet/lib/services/exchange/exchange_data_loading_service.dart
2024-10-01 12:55:50 -06:00

434 lines
13 KiB
Dart

/*
* This file is part of Stack Wallet.
*
* Copyright (c) 2023 Cypher Stack
* All Rights Reserved.
* The code is distributed under GPLv3 license, see LICENSE file for details.
* Generated by Cypher Stack on 2023-05-26
*
*/
import 'package:flutter/foundation.dart';
import 'package:isar/isar.dart';
import 'package:tuple/tuple.dart';
import '../../app_config.dart';
import '../../db/hive/db.dart';
import '../../models/exchange/active_pair.dart';
import '../../models/exchange/aggregate_currency.dart';
import '../../models/isar/exchange_cache/currency.dart';
import '../../models/isar/exchange_cache/pair.dart';
import '../../utilities/enums/exchange_rate_type_enum.dart';
import '../../utilities/logger.dart';
import '../../utilities/prefs.dart';
import '../../utilities/stack_file_system.dart';
import 'change_now/change_now_exchange.dart';
import 'majestic_bank/majestic_bank_exchange.dart';
import 'nanswap/nanswap_exchange.dart';
import 'trocador/trocador_exchange.dart';
class ExchangeDataLoadingService {
ExchangeDataLoadingService._();
static final ExchangeDataLoadingService _instance =
ExchangeDataLoadingService._();
static ExchangeDataLoadingService get instance => _instance;
Isar? _isar;
Isar get isar => _isar!;
VoidCallback? onLoadingError;
VoidCallback? onLoadingComplete;
static const int cacheVersion = 1;
static int get currentCacheVersion =>
DB.instance.get<dynamic>(
boxName: DB.boxNameDBInfo,
key: "exchange_data_cache_version",
) as int? ??
0;
Future<void> _updateCurrentCacheVersion(int version) async {
await DB.instance.put<dynamic>(
boxName: DB.boxNameDBInfo,
key: "exchange_data_cache_version",
value: version,
);
}
Future<void> initDB() async {
if (_isar != null) return;
await _isar?.close();
_isar = await Isar.open(
[
CurrencySchema,
// PairSchema,
],
directory: (await StackFileSystem.applicationIsarDirectory()).path,
// inspector: kDebugMode,
inspector: false,
name: "exchange_cache",
maxSizeMiB: 64,
);
}
Future<void> setCurrenciesIfEmpty(
ActivePair? pair,
ExchangeRateType rateType,
) async {
if (pair?.send == null && pair?.receive == null) {
if (await isar.currencies.count() > 0) {
pair?.setSend(
await getAggregateCurrency(
AppConfig.swapDefaults.from,
rateType,
null,
),
notifyListeners: false,
);
pair?.setReceive(
await getAggregateCurrency(
AppConfig.swapDefaults.to,
rateType,
null,
),
notifyListeners: false,
);
}
}
}
Future<AggregateCurrency?> getAggregateCurrency(
String ticker,
ExchangeRateType rateType,
String? contract,
) async {
final currencies = await ExchangeDataLoadingService.instance.isar.currencies
.filter()
.group(
(q) => rateType == ExchangeRateType.fixed
? q
.rateTypeEqualTo(SupportedRateType.both)
.or()
.rateTypeEqualTo(SupportedRateType.fixed)
: q
.rateTypeEqualTo(SupportedRateType.both)
.or()
.rateTypeEqualTo(SupportedRateType.estimated),
)
.and()
.tickerEqualTo(
ticker,
caseSensitive: false,
)
.and()
.tokenContractEqualTo(contract)
.findAll();
final items = currencies
.map((e) => Tuple2(e.exchangeName, e))
.toList(growable: false);
return items.isNotEmpty
? AggregateCurrency(exchangeCurrencyPairs: items)
: null;
}
bool get isLoading => _locked;
bool _locked = false;
Future<void> loadAll() async {
if (!_locked) {
_locked = true;
if (_isar == null) {
await initDB();
}
Logging.instance.log(
"ExchangeDataLoadingService.loadAll starting...",
level: LogLevel.Info,
);
final start = DateTime.now();
try {
/*
// Old exchange data loading code.
await Future.wait([
_loadChangeNowCurrencies(),
// _loadChangeNowFixedRatePairs(),
// _loadChangeNowEstimatedRatePairs(),
// loadSimpleswapFixedRateCurrencies(ref),
// loadSimpleswapFloatingRateCurrencies(ref),
loadMajesticBankCurrencies(),
loadTrocadorCurrencies(),
]);
// quicker to load available currencies on the fly for a specific base currency
// await _loadChangeNowFixedRatePairs();
// await _loadChangeNowEstimatedRatePairs();
*/
// Exchanges which support Tor just get treated normally.
final futures = [
loadMajesticBankCurrencies(),
loadTrocadorCurrencies(),
loadNanswapCurrencies(),
];
// If using Tor, don't load data for exchanges which don't support Tor.
//
// Add to this list when adding an exchange which doesn't supports Tor.
if (!Prefs.instance.useTor) {
futures.add(_loadChangeNowCurrencies());
}
// wait for all loading futures to complete
await Future.wait(futures);
Logging.instance.log(
"ExchangeDataLoadingService.loadAll finished in ${DateTime.now().difference(start).inSeconds} seconds",
level: LogLevel.Info,
);
onLoadingComplete?.call();
await _updateCurrentCacheVersion(cacheVersion);
} catch (e, s) {
Logging.instance.log(
"ExchangeDataLoadingService.loadAll failed after ${DateTime.now().difference(start).inSeconds} seconds: $e\n$s",
level: LogLevel.Error,
);
onLoadingError?.call();
}
_locked = false;
}
}
Future<void> _loadChangeNowCurrencies() async {
if (_isar == null) {
await initDB();
}
final exchange = ChangeNowExchange.instance;
final responseCurrencies = await exchange.getAllCurrencies(false);
if (responseCurrencies.value != null) {
await isar.writeTxn(() async {
final idsToDelete = await isar.currencies
.where()
.exchangeNameEqualTo(ChangeNowExchange.exchangeName)
.idProperty()
.findAll();
await isar.currencies.deleteAll(idsToDelete);
await isar.currencies.putAll(responseCurrencies.value!);
});
} else {
Logging.instance.log(
"Failed to load changeNOW currencies: ${responseCurrencies.exception?.message}",
level: LogLevel.Error,
);
return;
}
}
// Future<void> _loadChangeNowFixedRatePairs() async {
// final exchange = ChangeNowExchange.instance;
//
// final responsePairs = await compute(exchange.getAllPairs, true);
//
// if (responsePairs.value != null) {
// await isar.writeTxn(() async {
// final idsToDelete2 = await isar.pairs
// .where()
// .exchangeNameEqualTo(ChangeNowExchange.exchangeName)
// .filter()
// .rateTypeEqualTo(SupportedRateType.fixed)
// .idProperty()
// .findAll();
// await isar.pairs.deleteAll(idsToDelete2);
// await isar.pairs.putAll(responsePairs.value!);
// });
// } else {
// Logging.instance.log(
// "Failed to load changeNOW available fixed rate pairs: ${responsePairs.exception?.message}",
// level: LogLevel.Error);
// return;
// }
// }
// Future<void> _loadChangeNowEstimatedRatePairs() async {
// final exchange = ChangeNowExchange.instance;
//
// final responsePairs = await compute(exchange.getAllPairs, false);
//
// if (responsePairs.value != null) {
// await isar.writeTxn(() async {
// final idsToDelete = await isar.pairs
// .where()
// .exchangeNameEqualTo(ChangeNowExchange.exchangeName)
// .filter()
// .rateTypeEqualTo(SupportedRateType.estimated)
// .idProperty()
// .findAll();
// await isar.pairs.deleteAll(idsToDelete);
// await isar.pairs.putAll(responsePairs.value!);
// });
// } else {
// Logging.instance.log(
// "Failed to load changeNOW available floating rate pairs: ${responsePairs.exception?.message}",
// level: LogLevel.Error);
// return;
// }
// }
//
// Future<void> loadSimpleswapFloatingRateCurrencies(WidgetRef ref) async {
// final exchange = SimpleSwapExchange();
// final responseCurrencies = await exchange.getAllCurrencies(false);
//
// if (responseCurrencies.value != null) {
// ref
// .read(availableSimpleswapCurrenciesProvider)
// .updateFloatingCurrencies(responseCurrencies.value!);
//
// final responsePairs = await exchange.getAllPairs(false);
//
// if (responsePairs.value != null) {
// ref
// .read(availableSimpleswapCurrenciesProvider)
// .updateFloatingPairs(responsePairs.value!);
// } else {
// Logging.instance.log(
// "loadSimpleswapFloatingRateCurrencies: $responsePairs",
// level: LogLevel.Warning,
// );
// }
// } else {
// Logging.instance.log(
// "loadSimpleswapFloatingRateCurrencies: $responseCurrencies",
// level: LogLevel.Warning,
// );
// }
// }
//
// Future<void> loadSimpleswapFixedRateCurrencies(WidgetRef ref) async {
// final exchange = SimpleSwapExchange();
// final responseCurrencies = await exchange.getAllCurrencies(true);
//
// if (responseCurrencies.value != null) {
// ref
// .read(availableSimpleswapCurrenciesProvider)
// .updateFixedCurrencies(responseCurrencies.value!);
//
// final responsePairs = await exchange.getAllPairs(true);
//
// if (responsePairs.value != null) {
// ref
// .read(availableSimpleswapCurrenciesProvider)
// .updateFixedPairs(responsePairs.value!);
// } else {
// Logging.instance.log(
// "loadSimpleswapFixedRateCurrencies: $responsePairs",
// level: LogLevel.Warning,
// );
// }
// } else {
// Logging.instance.log(
// "loadSimpleswapFixedRateCurrencies: $responseCurrencies",
// level: LogLevel.Warning,
// );
// }
// }
Future<void> loadMajesticBankCurrencies() async {
if (_isar == null) {
await initDB();
}
final exchange = MajesticBankExchange.instance;
final responseCurrencies = await exchange.getAllCurrencies(false);
if (responseCurrencies.value != null) {
await isar.writeTxn(() async {
final idsToDelete = await isar.currencies
.where()
.exchangeNameEqualTo(MajesticBankExchange.exchangeName)
.idProperty()
.findAll();
await isar.currencies.deleteAll(idsToDelete);
await isar.currencies.putAll(responseCurrencies.value!);
});
} else {
Logging.instance.log(
"loadMajesticBankCurrencies: $responseCurrencies",
level: LogLevel.Warning,
);
}
}
Future<void> loadTrocadorCurrencies() async {
if (_isar == null) {
await initDB();
}
final exchange = TrocadorExchange.instance;
final responseCurrencies = await exchange.getAllCurrencies(false);
if (responseCurrencies.value != null) {
await isar.writeTxn(() async {
final idsToDelete = await isar.currencies
.where()
.exchangeNameEqualTo(TrocadorExchange.exchangeName)
.idProperty()
.findAll();
await isar.currencies.deleteAll(idsToDelete);
await isar.currencies.putAll(responseCurrencies.value!);
});
} else {
Logging.instance.log(
"loadTrocadorCurrencies: $responseCurrencies",
level: LogLevel.Warning,
);
}
}
Future<void> loadNanswapCurrencies() async {
if (_isar == null) {
await initDB();
}
final responseCurrencies =
await NanswapExchange.instance.getAllCurrencies(false);
if (responseCurrencies.value != null) {
await isar.writeTxn(() async {
final idsToDelete = await isar.currencies
.where()
.exchangeNameEqualTo(NanswapExchange.exchangeName)
.idProperty()
.findAll();
await isar.currencies.deleteAll(idsToDelete);
await isar.currencies.putAll(responseCurrencies.value!);
});
} else {
Logging.instance.log(
"loadNanswapCurrencies: $responseCurrencies",
level: LogLevel.Warning,
);
}
}
// Future<void> loadMajesticBankPairs() async {
// final exchange = MajesticBankExchange.instance;
//
// final responsePairs = await exchange.getAllPairs(false);
// if (responsePairs.value != null) {
// await isar.writeTxn(() async {
// final idsToDelete2 = await isar.pairs
// .where()
// .exchangeNameEqualTo(MajesticBankExchange.exchangeName)
// .idProperty()
// .findAll();
// await isar.pairs.deleteAll(idsToDelete2);
// await isar.pairs.putAll(responsePairs.value!);
// });
// } else {
// Logging.instance.log(
// "loadMajesticBankCurrencies: $responsePairs",
// level: LogLevel.Warning,
// );
// }
// }
}