migrate epiccash_wallet.dart to isar transactions, addresses, and utxos, as well as the cleaner balance model

This commit is contained in:
julian 2023-01-11 14:52:06 -06:00
parent 30d8f8b810
commit f551927603

View file

@ -6,15 +6,15 @@ import 'dart:isolate';
import 'package:decimal/decimal.dart'; import 'package:decimal/decimal.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter_libepiccash/epic_cash.dart'; import 'package:flutter_libepiccash/epic_cash.dart';
import 'package:hive/hive.dart';
import 'package:http/http.dart'; import 'package:http/http.dart';
import 'package:isar/isar.dart';
import 'package:mutex/mutex.dart'; import 'package:mutex/mutex.dart';
import 'package:stack_wallet_backup/generate_password.dart'; import 'package:stack_wallet_backup/generate_password.dart';
import 'package:stackwallet/hive/db.dart'; import 'package:stackwallet/hive/db.dart';
import 'package:stackwallet/models/balance.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart' as isar_models;
import 'package:stackwallet/models/node_model.dart'; import 'package:stackwallet/models/node_model.dart';
import 'package:stackwallet/models/paymint/fee_object_model.dart'; import 'package:stackwallet/models/paymint/fee_object_model.dart';
import 'package:stackwallet/models/paymint/transactions_model.dart';
import 'package:stackwallet/models/paymint/utxo_model.dart';
import 'package:stackwallet/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart';
import 'package:stackwallet/services/coins/coin_service.dart'; import 'package:stackwallet/services/coins/coin_service.dart';
import 'package:stackwallet/services/event_bus/events/global/blocks_remaining_event.dart'; import 'package:stackwallet/services/event_bus/events/global/blocks_remaining_event.dart';
@ -24,11 +24,11 @@ import 'package:stackwallet/services/event_bus/events/global/updated_in_backgrou
import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart'; import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart';
import 'package:stackwallet/services/event_bus/global_event_bus.dart'; import 'package:stackwallet/services/event_bus/global_event_bus.dart';
import 'package:stackwallet/services/node_service.dart'; import 'package:stackwallet/services/node_service.dart';
import 'package:stackwallet/services/price.dart';
import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/default_nodes.dart'; import 'package:stackwallet/utilities/default_nodes.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart'; import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/logger.dart'; import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/prefs.dart'; import 'package:stackwallet/utilities/prefs.dart';
import 'package:stackwallet/utilities/stack_file_system.dart'; import 'package:stackwallet/utilities/stack_file_system.dart';
@ -383,8 +383,8 @@ Future<dynamic> getSlates(String receiveAddress, String signature) async {
} }
} }
Future<bool> postCancel( Future<bool> postCancel(String receiveAddress, String slateId,
String receiveAddress, String slate_id, signature, sendersAddress) async { String? signature, String sendersAddress) async {
Logging.instance.log("postCancel", level: LogLevel.Info); Logging.instance.log("postCancel", level: LogLevel.Info);
final Client client = Client(); final Client client = Client();
try { try {
@ -395,18 +395,18 @@ Future<bool> postCancel(
"id": "0", "id": "0",
'receivingAddress': receiveAddress, 'receivingAddress': receiveAddress,
"signature": signature, "signature": signature,
'slate': slate_id, 'slate': slateId,
"sendersAddress": sendersAddress, "sendersAddress": sendersAddress,
}); });
final epicpost = await client.post( final epicPost = await client.post(
uri, uri,
headers: {'Content-Type': 'application/json'}, headers: {'Content-Type': 'application/json'},
body: body, body: body,
); );
// TODO: should the following be removed for security reasons in production? // TODO: should the following be removed for security reasons in production?
Logging.instance.log(epicpost.statusCode.toString(), level: LogLevel.Info); Logging.instance.log(epicPost.statusCode.toString(), level: LogLevel.Info);
Logging.instance.log(epicpost.body.toString(), level: LogLevel.Info); Logging.instance.log(epicPost.body.toString(), level: LogLevel.Info);
final response = jsonDecode(epicpost.body.toString()); final response = jsonDecode(epicPost.body.toString());
if (response['status'] == 'success') { if (response['status'] == 'success') {
return true; return true;
} else { } else {
@ -525,17 +525,16 @@ class EpicCashWallet extends CoinServiceAPI {
NodeModel? _epicNode; NodeModel? _epicNode;
late Isar isar;
EpicCashWallet( EpicCashWallet(
{required String walletId, {required String walletId,
required String walletName, required String walletName,
required Coin coin, required Coin coin,
PriceAPI? priceAPI,
required SecureStorageInterface secureStore}) { required SecureStorageInterface secureStore}) {
_walletId = walletId; _walletId = walletId;
_walletName = walletName; _walletName = walletName;
_coin = coin; _coin = coin;
_priceAPI = priceAPI ?? PriceAPI(Client());
_secureStore = secureStore; _secureStore = secureStore;
Logging.instance.log("$walletName isolate length: ${isolates.length}", Logging.instance.log("$walletName isolate length: ${isolates.length}",
@ -580,18 +579,6 @@ class EpicCashWallet extends CoinServiceAPI {
} }
} }
@override
Future<List<String>> get allOwnAddresses =>
_allOwnAddresses ??= _fetchAllOwnAddresses();
Future<List<String>>? _allOwnAddresses;
Future<List<String>> _fetchAllOwnAddresses() async {
List<String> addresses = [];
final ownAddress = await _getCurrentAddressForChain(0);
addresses.add(ownAddress);
return addresses;
}
late ReceivePort receivePort; late ReceivePort receivePort;
Future<String> startSync() async { Future<String> startSync() async {
@ -655,19 +642,6 @@ class EpicCashWallet extends CoinServiceAPI {
return walletBalances; return walletBalances;
} }
@override
Future<Decimal> get availableBalance async {
String walletBalances = await allWalletBalances();
var jsonBalances = json.decode(walletBalances);
final double spendable =
jsonBalances['amount_currently_spendable'] as double;
return Decimal.parse(spendable.toString());
}
@override
// TODO: implement balanceMinusMaxFee
Future<Decimal> get balanceMinusMaxFee => throw UnimplementedError();
Timer? timer; Timer? timer;
late Coin _coin; late Coin _coin;
@ -676,9 +650,7 @@ class EpicCashWallet extends CoinServiceAPI {
late SecureStorageInterface _secureStore; late SecureStorageInterface _secureStore;
late PriceAPI _priceAPI; Future<String> cancelPendingTransactionAndPost(String txSlateId) async {
Future<String> cancelPendingTransactionAndPost(String tx_slate_id) async {
final wallet = await _secureStore.read(key: '${_walletId}_wallet'); final wallet = await _secureStore.read(key: '${_walletId}_wallet');
final int? receivingIndex = DB.instance final int? receivingIndex = DB.instance
.get<dynamic>(boxName: walletId, key: "receivingIndex") as int?; .get<dynamic>(boxName: walletId, key: "receivingIndex") as int?;
@ -686,8 +658,8 @@ class EpicCashWallet extends CoinServiceAPI {
await _secureStore.read(key: '${_walletId}_epicboxConfig'); await _secureStore.read(key: '${_walletId}_epicboxConfig');
final slatesToCommits = await getSlatesToCommits(); final slatesToCommits = await getSlatesToCommits();
final receiveAddress = slatesToCommits[tx_slate_id]['to'] as String; final receiveAddress = slatesToCommits[txSlateId]['to'] as String;
final sendersAddress = slatesToCommits[tx_slate_id]['from'] as String; final sendersAddress = slatesToCommits[txSlateId]['from'] as String;
int? currentReceivingIndex; int? currentReceivingIndex;
for (int i = 0; i <= receivingIndex!; i++) { for (int i = 0; i <= receivingIndex!; i++) {
@ -722,11 +694,10 @@ class EpicCashWallet extends CoinServiceAPI {
String? signature = subscribeRequest['signature'] as String?; String? signature = subscribeRequest['signature'] as String?;
String? result; String? result;
try { try {
result = await cancelPendingTransaction(tx_slate_id); result = await cancelPendingTransaction(txSlateId);
Logging.instance.log("result?: $result", level: LogLevel.Info); Logging.instance.log("result?: $result", level: LogLevel.Info);
if (!(result.toLowerCase().contains("error"))) { if (!(result.toLowerCase().contains("error"))) {
await postCancel( await postCancel(receiveAddress, txSlateId, signature, sendersAddress);
receiveAddress, tx_slate_id, signature, sendersAddress);
} }
} catch (e, s) { } catch (e, s) {
Logging.instance.log("$e, $s", level: LogLevel.Error); Logging.instance.log("$e, $s", level: LogLevel.Error);
@ -736,7 +707,7 @@ class EpicCashWallet extends CoinServiceAPI {
// //
/// returns an empty String on success, error message on failure /// returns an empty String on success, error message on failure
Future<String> cancelPendingTransaction(String tx_slate_id) async { Future<String> cancelPendingTransaction(String txSlateId) async {
final String wallet = final String wallet =
(await _secureStore.read(key: '${_walletId}_wallet'))!; (await _secureStore.read(key: '${_walletId}_wallet'))!;
@ -746,7 +717,7 @@ class EpicCashWallet extends CoinServiceAPI {
_cancelTransactionWrapper, _cancelTransactionWrapper,
Tuple2( Tuple2(
wallet, wallet,
tx_slate_id, txSlateId,
), ),
); );
}); });
@ -847,12 +818,18 @@ class EpicCashWallet extends CoinServiceAPI {
final txLogEntry = json.decode(tx as String); final txLogEntry = json.decode(tx as String);
final txLogEntryFirst = txLogEntry[0]; final txLogEntryFirst = txLogEntry[0];
Logger.print("TX_LOG_ENTRY_IS $txLogEntryFirst"); Logger.print("TX_LOG_ENTRY_IS $txLogEntryFirst");
final wallet = await Hive.openBox<dynamic>(_walletId); final slateToAddresses = DB.instance.get<dynamic>(
final slateToAddresses = boxName: walletId,
(await wallet.get("slate_to_address")) as Map? ?? {}; key: "slate_to_address",
) as Map? ??
{};
final slateId = txLogEntryFirst['tx_slate_id'] as String; final slateId = txLogEntryFirst['tx_slate_id'] as String;
slateToAddresses[slateId] = txData['addresss']; slateToAddresses[slateId] = txData['addresss'];
await wallet.put('slate_to_address', slateToAddresses); await DB.instance.put<dynamic>(
boxName: walletId,
key: "slate_to_address",
value: slateToAddresses,
);
final slatesToCommits = await getSlatesToCommits(); final slatesToCommits = await getSlatesToCommits();
String? commitId = slatesToCommits[slateId]?['commitId'] as String?; String? commitId = slatesToCommits[slateId]?['commitId'] as String?;
Logging.instance.log("sent commitId: $commitId", level: LogLevel.Info); Logging.instance.log("sent commitId: $commitId", level: LogLevel.Info);
@ -979,6 +956,22 @@ class EpicCashWallet extends CoinServiceAPI {
return; return;
} }
Future<void> _isarInit() async {
isar = await Isar.open(
[
isar_models.TransactionSchema,
isar_models.TransactionNoteSchema,
isar_models.InputSchema,
isar_models.OutputSchema,
isar_models.UTXOSchema,
isar_models.AddressSchema,
],
directory: (await StackFileSystem.applicationIsarDirectory()).path,
inspector: false,
name: walletId,
);
}
@override @override
Future<void> initializeExisting() async { Future<void> initializeExisting() async {
Logging.instance.log("Opening existing ${coin.prettyName} wallet", Logging.instance.log("Opening existing ${coin.prettyName} wallet",
@ -998,12 +991,8 @@ class EpicCashWallet extends CoinServiceAPI {
} }
await _prefs.init(); await _prefs.init();
await updateNode(false); await updateNode(false);
final data = await _isarInit();
DB.instance.get<dynamic>(boxName: walletId, key: "latest_tx_model") await _refreshBalance();
as TransactionData?;
if (data != null) {
_transactionData = Future(() => data);
}
// TODO: is there anything else that should be set up here whenever this wallet is first loaded again? // TODO: is there anything else that should be set up here whenever this wallet is first loaded again?
} }
@ -1148,15 +1137,6 @@ class EpicCashWallet extends CoinServiceAPI {
@override @override
Future<List<String>> get mnemonic => _getMnemonicList(); Future<List<String>> get mnemonic => _getMnemonicList();
@override
Future<Decimal> get pendingBalance async {
String walletBalances = await allWalletBalances();
final jsonBalances = json.decode(walletBalances);
final double pending =
jsonBalances['amount_awaiting_confirmation'] as double;
return Decimal.parse(pending.toString());
}
@override @override
Future<Map<String, dynamic>> prepareSend( Future<Map<String, dynamic>> prepareSend(
{required String address, {required String address,
@ -1494,6 +1474,7 @@ class EpicCashWallet extends CoinServiceAPI {
return latestHeight!; return latestHeight!;
} }
@override
int get storedChainHeight { int get storedChainHeight {
return DB.instance.get<dynamic>(boxName: walletId, key: "storedChainHeight") return DB.instance.get<dynamic>(boxName: walletId, key: "storedChainHeight")
as int? ?? as int? ??
@ -1676,7 +1657,7 @@ class EpicCashWallet extends CoinServiceAPI {
currentAddress, subscribeRequest['signature'] as String); currentAddress, subscribeRequest['signature'] as String);
if (unprocessedSlates == null || unprocessedSlates is! List) { if (unprocessedSlates == null || unprocessedSlates is! List) {
Logging.instance.log( Logging.instance.log(
"index $currentReceivingIndex at $currentReceivingAddress does not have any slates", "index $currentReceivingIndex at ${await currentReceivingAddress} does not have any slates",
level: LogLevel.Info); level: LogLevel.Info);
continue; continue;
} }
@ -1814,7 +1795,7 @@ class EpicCashWallet extends CoinServiceAPI {
await _secureStore.read(key: '${_walletId}_epicboxConfig'); await _secureStore.read(key: '${_walletId}_epicboxConfig');
final int? receivingIndex = DB.instance final int? receivingIndex = DB.instance
.get<dynamic>(boxName: walletId, key: "receivingIndex") as int?; .get<dynamic>(boxName: walletId, key: "receivingIndex") as int?;
final tData = await _transactionData;
for (int currentReceivingIndex = 0; for (int currentReceivingIndex = 0;
receivingIndex != null && currentReceivingIndex <= receivingIndex; receivingIndex != null && currentReceivingIndex <= receivingIndex;
currentReceivingIndex++) { currentReceivingIndex++) {
@ -1847,16 +1828,16 @@ class EpicCashWallet extends CoinServiceAPI {
final slatesToCommits = await getSlatesToCommits(); final slatesToCommits = await getSlatesToCommits();
for (final cancel in cancels as List<dynamic>) { for (final cancel in cancels as List<dynamic>) {
final tx_slate_id = cancel.keys.first as String; final txSlateId = cancel.keys.first as String;
if (slatesToCommits[tx_slate_id] == null) { if (slatesToCommits[txSlateId] == null) {
continue; continue;
} }
final cancelRequestSender = ((cancel as Map).values.first) as String; final cancelRequestSender = ((cancel as Map).values.first) as String;
final receiveAddressFromMap = final receiveAddressFromMap =
slatesToCommits[tx_slate_id]['to'] as String; slatesToCommits[txSlateId]['to'] as String;
final sendersAddressFromMap = final sendersAddressFromMap =
slatesToCommits[tx_slate_id]['from'] as String; slatesToCommits[txSlateId]['from'] as String;
final commitId = slatesToCommits[tx_slate_id]['commitId'] as String; final commitId = slatesToCommits[txSlateId]['commitId'] as String;
if (sendersAddressFromMap != cancelRequestSender) { if (sendersAddressFromMap != cancelRequestSender) {
Logging.instance.log("this was not signed by the correct address", Logging.instance.log("this was not signed by the correct address",
@ -1864,11 +1845,12 @@ class EpicCashWallet extends CoinServiceAPI {
continue; continue;
} }
String? result;
try { try {
result = await cancelPendingTransaction(tx_slate_id); await cancelPendingTransaction(txSlateId);
if (tData?.findTransaction(commitId)?.isCancelled ?? false == true) { final tx =
await deleteCancels(receiveAddressFromMap, signature, tx_slate_id); await isar.transactions.where().txidEqualTo(commitId).findFirst();
if ((tx?.isCancelled ?? false) == true) {
await deleteCancels(receiveAddressFromMap, signature, txSlateId);
} }
} catch (e, s) { } catch (e, s) {
Logging.instance.log("$e, $s", level: LogLevel.Error); Logging.instance.log("$e, $s", level: LogLevel.Error);
@ -1933,7 +1915,7 @@ class EpicCashWallet extends CoinServiceAPI {
await processAllSlates(); await processAllSlates();
await processAllCancels(); await processAllCancels();
startSync(); unawaited(startSync());
GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.0, walletId)); GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.0, walletId));
@ -1955,16 +1937,16 @@ class EpicCashWallet extends CoinServiceAPI {
unawaited(updateStoredChainHeight(newHeight: currentHeight)); unawaited(updateStoredChainHeight(newHeight: currentHeight));
} }
final newTxData = _fetchTransactionData(); await _refreshTransactions();
GlobalEventBus.instance GlobalEventBus.instance
.fire(RefreshPercentChangedEvent(0.50, walletId)); .fire(RefreshPercentChangedEvent(0.50, walletId));
_transactionData = Future(() => newTxData);
GlobalEventBus.instance.fire(UpdatedInBackgroundEvent( GlobalEventBus.instance.fire(UpdatedInBackgroundEvent(
"New data found in $walletName in background!", walletId)); "New data found in $walletName in background!", walletId));
} }
await _refreshBalance();
GlobalEventBus.instance.fire(RefreshPercentChangedEvent(1.0, walletId)); GlobalEventBus.instance.fire(RefreshPercentChangedEvent(1.0, walletId));
GlobalEventBus.instance.fire( GlobalEventBus.instance.fire(
WalletSyncStatusChangedEvent( WalletSyncStatusChangedEvent(
@ -2020,15 +2002,6 @@ class EpicCashWallet extends CoinServiceAPI {
// TODO: do a quick check to see if there is any new data that would require a refresh // TODO: do a quick check to see if there is any new data that would require a refresh
} }
@override
Future<String> send(
{required String toAddress,
required int amount,
Map<String, String> args = const {}}) {
// TODO: implement send
throw UnimplementedError();
}
@override @override
Future<bool> testNetworkConnection() async { Future<bool> testNetworkConnection() async {
try { try {
@ -2084,18 +2057,8 @@ class EpicCashWallet extends CoinServiceAPI {
@override @override
bool get isConnected => _isConnected; bool get isConnected => _isConnected;
@override Future<void> _refreshTransactions() async {
Future<Decimal> get totalBalance async { // final currentChainHeight = await chainHeight;
String walletBalances = await allWalletBalances();
var jsonBalances = json.decode(walletBalances);
double total = jsonBalances['total'] as double;
double awaiting = jsonBalances['amount_awaiting_finalization'] as double;
total = total + awaiting;
return Decimal.parse(total.toString());
}
Future<TransactionData> _fetchTransactionData() async {
final currentChainHeight = await chainHeight;
final wallet = await _secureStore.read(key: '${_walletId}_wallet'); final wallet = await _secureStore.read(key: '${_walletId}_wallet');
const refreshFromNode = 0; const refreshFromNode = 0;
@ -2121,38 +2084,28 @@ class EpicCashWallet extends CoinServiceAPI {
// return message; // return message;
final String transactions = message['result'] as String; final String transactions = message['result'] as String;
final jsonTransactions = json.decode(transactions) as List; final jsonTransactions = json.decode(transactions) as List;
// for (var el in jsonTransactions) {
// Logging.instance.log("gettran: $el",
// normalLength: false, addToDebugMessagesDB: true);
// }
final priceData = final List<isar_models.Transaction> midSortedArray = [];
await _priceAPI.getPricesAnd24hChange(baseCurrency: _prefs.currency);
Decimal currentPrice = priceData[coin]?.item1 ?? Decimal.zero;
final List<Map<String, dynamic>> midSortedArray = [];
int latestTxnBlockHeight = int latestTxnBlockHeight =
DB.instance.get<dynamic>(boxName: walletId, key: "storedTxnDataHeight") DB.instance.get<dynamic>(boxName: walletId, key: "storedTxnDataHeight")
as int? ?? as int? ??
0; 0;
final slatesToCommits = await getSlatesToCommits(); final slatesToCommits = await getSlatesToCommits();
final cachedTransactions =
DB.instance.get<dynamic>(boxName: walletId, key: 'latest_tx_model')
as TransactionData?;
var cachedMap = cachedTransactions?.getAllTransactions();
for (var tx in jsonTransactions) { for (var tx in jsonTransactions) {
Logging.instance.log("tx: $tx", level: LogLevel.Info); Logging.instance.log("tx: $tx", level: LogLevel.Info);
final txHeight = tx["kernel_lookup_min_height"] as int? ?? 0; final txHeight = tx["kernel_lookup_min_height"] as int? ?? 0;
// TODO: does "confirmed" mean finalized? If so please remove this todo // // TODO: does "confirmed" mean finalized? If so please remove this todo
final isConfirmed = tx["confirmed"] as bool; // final isConfirmed = tx["confirmed"] as bool;
// TODO: since we are now caching tx history in hive are we losing anything by skipping here? // // TODO: since we are now caching tx history in hive are we losing anything by skipping here?
// TODO: we can skip this filtering if it causes issues as the cache is later merged with updated data anyways // // TODO: we can skip this filtering if it causes issues as the cache is later merged with updated data anyways
// this would just make processing and updating cache more efficient // // this would just make processing and updating cache more efficient
if (txHeight > 0 && // if (txHeight > 0 &&
txHeight < latestTxnBlockHeight - MINIMUM_CONFIRMATIONS && // txHeight < latestTxnBlockHeight - MINIMUM_CONFIRMATIONS &&
isConfirmed) { // isConfirmed) {
continue; // continue;
} // }
// Logging.instance.log("Transactions listed below"); // Logging.instance.log("Transactions listed below");
// Logging.instance.log(jsonTransactions); // Logging.instance.log(jsonTransactions);
int amt = 0; int amt = 0;
@ -2165,20 +2118,18 @@ class EpicCashWallet extends CoinServiceAPI {
int fee = int.parse((tx['fee'] ?? "0") as String); int fee = int.parse((tx['fee'] ?? "0") as String);
amt = debit - credit - fee; amt = debit - credit - fee;
} }
final String worthNow =
(currentPrice * Decimal.parse(amt.toString())).toStringAsFixed(2);
DateTime dt = DateTime.parse(tx["creation_ts"] as String); DateTime dt = DateTime.parse(tx["creation_ts"] as String);
Map<String, dynamic> midSortedTx = {}; final txn = isar_models.Transaction();
midSortedTx["txType"] = (tx["tx_type"] == "TxReceived" || txn.type = (tx["tx_type"] == "TxReceived" ||
tx["tx_type"] == "TxReceivedCancelled") tx["tx_type"] == "TxReceivedCancelled")
? "Received" ? isar_models.TransactionType.incoming
: "Sent"; : isar_models.TransactionType.outgoing;
String? slateId = tx['tx_slate_id'] as String?; String? slateId = tx['tx_slate_id'] as String?;
String? address = slatesToCommits[slateId] String? address = slatesToCommits[slateId]
?[midSortedTx["txType"] == "TxReceived" ? "from" : "to"] ?[tx["tx_type"] == "TxReceived" ? "from" : "to"] as String? ??
as String? ??
""; "";
String? commitId = slatesToCommits[slateId]?['commitId'] as String?; String? commitId = slatesToCommits[slateId]?['commitId'] as String?;
Logging.instance.log( Logging.instance.log(
@ -2188,116 +2139,99 @@ class EpicCashWallet extends CoinServiceAPI {
bool isCancelled = tx["tx_type"] == "TxSentCancelled" || bool isCancelled = tx["tx_type"] == "TxSentCancelled" ||
tx["tx_type"] == "TxReceivedCancelled"; tx["tx_type"] == "TxReceivedCancelled";
midSortedTx["slateId"] = slateId; txn.slateId = slateId;
midSortedTx["isCancelled"] = isCancelled; txn.isCancelled = isCancelled;
midSortedTx["txid"] = commitId ?? tx["id"].toString(); txn.txid = commitId ?? tx["id"].toString();
midSortedTx["confirmed_status"] = isConfirmed; txn.timestamp = (dt.millisecondsSinceEpoch ~/ 1000);
midSortedTx["timestamp"] = (dt.millisecondsSinceEpoch ~/ 1000); txn.amount = amt;
midSortedTx["amount"] = amt; txn.fee = (tx["fee"] == null) ? 0 : int.parse(tx["fee"] as String);
midSortedTx["worthNow"] = worthNow; txn.address =
midSortedTx["worthAtBlockTimestamp"] = worthNow;
midSortedTx["fees"] =
(tx["fee"] == null) ? 0 : int.parse(tx["fee"] as String);
midSortedTx["address"] =
""; // for this when you send a transaction you will just need to save in a hashmap in hive with the key being the txid, and the value being the address it was sent to. then you can look this value up right here in your hashmap. ""; // for this when you send a transaction you will just need to save in a hashmap in hive with the key being the txid, and the value being the address it was sent to. then you can look this value up right here in your hashmap.
midSortedTx["address"] = address; txn.address = address;
midSortedTx["height"] = txHeight; txn.height = txHeight;
int confirmations = 0;
try {
confirmations = currentChainHeight - txHeight;
} catch (e, s) {
//todo: come back to this
debugPrint("$e $s");
}
midSortedTx["confirmations"] = confirmations;
midSortedTx["inputSize"] = tx["num_inputs"]; //
midSortedTx["outputSize"] = tx["num_outputs"]; // midSortedTx["inputSize"] = tx["num_inputs"];
midSortedTx["aliens"] = <dynamic>[]; // midSortedTx["outputSize"] = tx["num_outputs"];
midSortedTx["inputs"] = <dynamic>[]; // midSortedTx["aliens"] = <dynamic>[];
midSortedTx["outputs"] = <dynamic>[]; // midSortedTx["inputs"] = <dynamic>[];
midSortedTx["tx_slate_id"] = tx["tx_slate_id"]; // midSortedTx["outputs"] = <dynamic>[];
midSortedTx["key_id"] = tx["parent_key_id"];
midSortedTx["otherData"] = tx["id"].toString(); // key id not used afaik?
// midSortedTx["key_id"] = tx["parent_key_id"];
txn.otherData = tx["id"].toString();
if (txHeight >= latestTxnBlockHeight) { if (txHeight >= latestTxnBlockHeight) {
latestTxnBlockHeight = txHeight; latestTxnBlockHeight = txHeight;
} }
midSortedArray.add(midSortedTx); midSortedArray.add(txn);
cachedMap?.remove(tx["id"].toString()); // cachedMap?.remove(tx["id"].toString());
cachedMap?.remove(commitId); // cachedMap?.remove(commitId);
Logging.instance.log("cmap: $cachedMap", level: LogLevel.Info); // Logging.instance.log("cmap: $cachedMap", level: LogLevel.Info);
} }
midSortedArray await isar
.sort((a, b) => (b["timestamp"] as int) - (a["timestamp"] as int)); .writeTxn(() async => await isar.transactions.putAll(midSortedArray));
final Map<String, dynamic> result = {"dateTimeChunks": <dynamic>[]}; // midSortedArray
final dateArray = <dynamic>[]; // .sort((a, b) => (b["timestamp"] as int) - (a["timestamp"] as int));
//
for (int i = 0; i < midSortedArray.length; i++) { // final Map<String, dynamic> result = {"dateTimeChunks": <dynamic>[]};
final txObject = midSortedArray[i]; // final dateArray = <dynamic>[];
final date = extractDateFromTimestamp(txObject["timestamp"] as int); //
// for (int i = 0; i < midSortedArray.length; i++) {
final txTimeArray = [txObject["timestamp"], date]; // final txObject = midSortedArray[i];
// final date = extractDateFromTimestamp(txObject["timestamp"] as int);
if (dateArray.contains(txTimeArray[1])) { //
result["dateTimeChunks"].forEach((dynamic chunk) { // final txTimeArray = [txObject["timestamp"], date];
if (extractDateFromTimestamp(chunk["timestamp"] as int) == //
txTimeArray[1]) { // if (dateArray.contains(txTimeArray[1])) {
if (chunk["transactions"] == null) { // result["dateTimeChunks"].forEach((dynamic chunk) {
chunk["transactions"] = <Map<String, dynamic>>[]; // if (extractDateFromTimestamp(chunk["timestamp"] as int) ==
} // txTimeArray[1]) {
chunk["transactions"].add(txObject); // if (chunk["transactions"] == null) {
} // chunk["transactions"] = <Map<String, dynamic>>[];
}); // }
} else { // chunk["transactions"].add(txObject);
dateArray.add(txTimeArray[1]); // }
// });
final chunk = { // } else {
"timestamp": txTimeArray[0], // dateArray.add(txTimeArray[1]);
"transactions": [txObject], //
}; // final chunk = {
// "timestamp": txTimeArray[0],
// result["dateTimeChunks"]. // "transactions": [txObject],
result["dateTimeChunks"].add(chunk); // };
} //
} // // result["dateTimeChunks"].
final transactionsMap = // result["dateTimeChunks"].add(chunk);
TransactionData.fromJson(result).getAllTransactions(); // }
if (cachedMap != null) { // }
transactionsMap.addAll(cachedMap); // final transactionsMap =
} // TransactionData.fromJson(result).getAllTransactions();
// if (cachedMap != null) {
final txModel = TransactionData.fromMap(transactionsMap); // transactionsMap.addAll(cachedMap);
// }
await DB.instance.put<dynamic>( //
boxName: walletId, // final txModel = TransactionData.fromMap(transactionsMap);
key: 'storedTxnDataHeight', //
value: latestTxnBlockHeight); // await DB.instance.put<dynamic>(
await DB.instance.put<dynamic>( // boxName: walletId,
boxName: walletId, key: 'latest_tx_model', value: txModel); // key: 'storedTxnDataHeight',
// value: latestTxnBlockHeight);
return txModel; // await DB.instance.put<dynamic>(
// boxName: walletId, key: 'latest_tx_model', value: txModel);
//
// return txModel;
} }
@override
Future<TransactionData> get transactionData =>
_transactionData ??= _fetchTransactionData();
Future<TransactionData>? _transactionData;
// not used in epic
TransactionData? cachedTxData;
@override @override
Future<void> updateSentCachedTxData(Map<String, dynamic> txData) async { Future<void> updateSentCachedTxData(Map<String, dynamic> txData) async {
// not used in epic // not used in epic
} }
@override
Future<List<UtxoObject>> get unspentOutputs => throw UnimplementedError();
@override @override
bool validateAddress(String address) { bool validateAddress(String address) {
if (address.startsWith("http://") || address.startsWith("https://")) { if (address.startsWith("http://") || address.startsWith("https://")) {
@ -2345,7 +2279,6 @@ class EpicCashWallet extends CoinServiceAPI {
@override @override
Future<int> estimateFeeFor(int satoshiAmount, int feeRate) async { Future<int> estimateFeeFor(int satoshiAmount, int feeRate) async {
int currentFee = await nativeFee(satoshiAmount, ifErrorEstimateFee: true); int currentFee = await nativeFee(satoshiAmount, ifErrorEstimateFee: true);
// TODO: implement this
return currentFee; return currentFee;
} }
@ -2353,18 +2286,6 @@ class EpicCashWallet extends CoinServiceAPI {
@override @override
Future<bool> generateNewAddress() async { Future<bool> generateNewAddress() async {
try { try {
// await incrementAddressIndexForChain(
// 0); // First increment the receiving index
// final newReceivingIndex =
// DB.instance.get<dynamic>(boxName: walletId, key: 'receivingIndex')
// as int; // Check the new receiving index
// final newReceivingAddress = await _generateAddressForChain(0,
// newReceivingIndex); // Use new index to derive a new receiving address
// await addToAddressesArrayForChain(newReceivingAddress,
// 0); // Add that new receiving address to the array of receiving addresses
// _currentReceivingAddress = Future(() =>
// newReceivingAddress); // Set the new receiving address that the service
return true; return true;
} catch (e, s) { } catch (e, s) {
Logging.instance.log( Logging.instance.log(
@ -2373,4 +2294,47 @@ class EpicCashWallet extends CoinServiceAPI {
return false; return false;
} }
} }
Future<void> _refreshBalance() async {
String walletBalances = await allWalletBalances();
var jsonBalances = json.decode(walletBalances);
final spendable =
(jsonBalances['amount_currently_spendable'] as double).toString();
final pending =
(jsonBalances['amount_awaiting_confirmation'] as double).toString();
final total = (jsonBalances['total'] as double).toString();
final awaiting =
(jsonBalances['amount_awaiting_finalization'] as double).toString();
_balance = Balance(
coin: coin,
total: Format.decimalAmountToSatoshis(
Decimal.parse(total) + Decimal.parse(awaiting),
coin,
),
spendable: Format.decimalAmountToSatoshis(
Decimal.parse(spendable),
coin,
),
blockedTotal: 0,
pendingSpendable: Format.decimalAmountToSatoshis(
Decimal.parse(pending),
coin,
),
);
}
@override
Balance get balance => _balance!;
Balance? _balance;
@override
Future<List<isar_models.UTXO>> get utxos => throw UnimplementedError();
@override
Future<List<isar_models.Transaction>> get transactions =>
isar.transactions.where().findAll();
} }