update other coin's parse transaction and db update

This commit is contained in:
julian 2023-01-13 15:36:19 -06:00
parent e14c362574
commit 3414c71741
10 changed files with 267 additions and 1240 deletions

View file

@ -2064,7 +2064,7 @@ class BitcoinWallet extends CoinServiceAPI with WalletCache, WalletDB {
final List<
Tuple4<isar_models.Transaction, List<isar_models.Output>,
List<isar_models.Input>, isar_models.Address>> txnsData = [];
List<isar_models.Input>, isar_models.Address?>> txnsData = [];
for (final txObject in allTransactions) {
final data = await parseTransaction(
@ -2077,40 +2077,7 @@ class BitcoinWallet extends CoinServiceAPI with WalletCache, WalletDB {
txnsData.add(data);
}
await isar.writeTxn(() async {
for (final data in txnsData) {
final tx = data.item1;
// save transaction
await isar.transactions.put(tx);
// link and save outputs
if (data.item2.isNotEmpty) {
await isar.outputs.putAll(data.item2);
tx.outputs.addAll(data.item2);
await tx.outputs.save();
}
// link and save inputs
if (data.item3.isNotEmpty) {
await isar.inputs.putAll(data.item3);
tx.inputs.addAll(data.item3);
await tx.inputs.save();
}
// check if address exists in db and add if it does not
if (await isar.addresses
.where()
.valueEqualTo(data.item4.value)
.findFirst() ==
null) {
await isar.addresses.put(data.item4);
}
// link and save address
tx.address.value = data.item4;
await tx.address.save();
}
});
await addNewTransactionData(txnsData);
}
int estimateTxFee({required int vSize, required int feeRatePerKB}) {

View file

@ -1940,21 +1940,31 @@ class BitcoinCashWallet extends CoinServiceAPI with WalletCache, WalletDB {
List<Map<String, dynamic>> allTransactions = [];
for (final txHash in allTxHashes) {
final tx = await cachedElectrumXClient.getTransaction(
txHash: txHash["tx_hash"] as String,
verbose: true,
coin: coin,
);
final currentHeight = await chainHeight;
// Logging.instance.log("TRANSACTION: ${jsonEncode(tx)}");
if (!_duplicateTxCheck(allTransactions, tx["txid"] as String)) {
tx["address"] = await isar.addresses
.filter()
.valueEqualTo(txHash["address"] as String)
.findFirst();
tx["height"] = txHash["height"];
allTransactions.add(tx);
for (final txHash in allTxHashes) {
final storedTx = await isar.transactions
.where()
.txidEqualTo(txHash["tx_hash"] as String)
.findFirst();
if (storedTx == null ||
!storedTx.isConfirmed(currentHeight, MINIMUM_CONFIRMATIONS)) {
final tx = await cachedElectrumXClient.getTransaction(
txHash: txHash["tx_hash"] as String,
verbose: true,
coin: coin,
);
// Logging.instance.log("TRANSACTION: ${jsonEncode(tx)}");
if (!_duplicateTxCheck(allTransactions, tx["txid"] as String)) {
tx["address"] = await isar.addresses
.filter()
.valueEqualTo(txHash["address"] as String)
.findFirst();
tx["height"] = txHash["height"];
allTransactions.add(tx);
}
}
}
//
@ -1964,7 +1974,9 @@ class BitcoinCashWallet extends CoinServiceAPI with WalletCache, WalletDB {
// Logging.instance.log("allTransactions length: ${allTransactions.length}",
// level: LogLevel.Info);
final List<isar_models.Transaction> txns = [];
final List<
Tuple4<isar_models.Transaction, List<isar_models.Output>,
List<isar_models.Input>, isar_models.Address?>> txns = [];
for (final txData in allTransactions) {
Set<String> inputAddresses = {};
@ -2060,6 +2072,10 @@ class BitcoinCashWallet extends CoinServiceAPI with WalletCache, WalletDB {
tx.timestamp = txData["blocktime"] as int? ??
(DateTime.now().millisecondsSinceEpoch ~/ 1000);
// this is the address initially used to fetch the txid
isar_models.Address transactionAddress =
txData["address"] as isar_models.Address;
if (mySentFromAddresses.isNotEmpty && myReceivedOnAddresses.isNotEmpty) {
// tx is sent to self
tx.type = isar_models.TransactionType.sentToSelf;
@ -2069,6 +2085,17 @@ class BitcoinCashWallet extends CoinServiceAPI with WalletCache, WalletDB {
// outgoing tx
tx.type = isar_models.TransactionType.outgoing;
tx.amount = amountSentFromWallet - changeAmount - fee;
final possible =
outputAddresses.difference(myChangeReceivedOnAddresses).first;
if (transactionAddress.value != possible) {
transactionAddress = isar_models.Address()
..value = possible
..derivationIndex = -1
..subType = AddressSubType.nonWallet
..type = AddressType.nonWallet
..publicKey = [];
}
} else {
// incoming tx
tx.type = isar_models.TransactionType.incoming;
@ -2079,7 +2106,9 @@ class BitcoinCashWallet extends CoinServiceAPI with WalletCache, WalletDB {
tx.subType = isar_models.TransactionSubType.none;
tx.fee = fee;
tx.address.value = txData["address"] as isar_models.Address;
List<isar_models.Input> inputs = [];
List<isar_models.Output> outputs = [];
for (final json in txData["vin"] as List) {
bool isCoinBase = json['coinbase'] != null;
@ -2092,7 +2121,7 @@ class BitcoinCashWallet extends CoinServiceAPI with WalletCache, WalletDB {
isCoinBase ? isCoinBase : json['is_coinbase'] as bool?;
input.sequence = json['sequence'] as int?;
input.innerRedeemScriptAsm = json['innerRedeemscriptAsm'] as String?;
tx.inputs.add(input);
inputs.add(input);
}
for (final json in txData["vout"] as List) {
@ -2107,7 +2136,7 @@ class BitcoinCashWallet extends CoinServiceAPI with WalletCache, WalletDB {
Decimal.parse(json["value"].toString()),
coin,
);
tx.outputs.add(output);
outputs.add(output);
}
tx.height = txData["height"] as int?;
@ -2116,12 +2145,10 @@ class BitcoinCashWallet extends CoinServiceAPI with WalletCache, WalletDB {
tx.slateId = null;
tx.otherData = null;
txns.add(tx);
txns.add(Tuple4(tx, outputs, inputs, transactionAddress));
}
await isar.writeTxn(() async {
await isar.transactions.putAll(txns);
});
await addNewTransactionData(txns);
}
int estimateTxFee({required int vSize, required int feeRatePerKB}) {

View file

@ -1846,18 +1846,27 @@ class DogecoinWallet extends CoinServiceAPI with WalletCache, WalletDB {
await fastFetch(hashes);
List<Map<String, dynamic>> allTransactions = [];
final currentHeight = await chainHeight;
for (final txHash in allTxHashes) {
final tx = await cachedElectrumXClient.getTransaction(
txHash: txHash["tx_hash"] as String,
verbose: true,
coin: coin,
);
final storedTx = await isar.transactions
.where()
.txidEqualTo(txHash["tx_hash"] as String)
.findFirst();
if (!_duplicateTxCheck(allTransactions, tx["txid"] as String)) {
tx["address"] = txHash["address"];
tx["height"] = txHash["height"];
allTransactions.add(tx);
if (storedTx == null ||
!storedTx.isConfirmed(currentHeight, MINIMUM_CONFIRMATIONS)) {
final tx = await cachedElectrumXClient.getTransaction(
txHash: txHash["tx_hash"] as String,
verbose: true,
coin: coin,
);
if (!_duplicateTxCheck(allTransactions, tx["txid"] as String)) {
tx["address"] = txHash["address"];
tx["height"] = txHash["height"];
allTransactions.add(tx);
}
}
}
@ -1871,6 +1880,10 @@ class DogecoinWallet extends CoinServiceAPI with WalletCache, WalletDB {
}
await fastFetch(vHashes.toList());
final List<
Tuple4<isar_models.Transaction, List<isar_models.Output>,
List<isar_models.Input>, isar_models.Address?>> txns = [];
for (final txObject in allTransactions) {
final txn = await parseTransaction(
txObject,
@ -1880,17 +1893,10 @@ class DogecoinWallet extends CoinServiceAPI with WalletCache, WalletDB {
MINIMUM_CONFIRMATIONS,
);
// final tx = await isar.transactions
// .filter()
// .txidMatches(midSortedTx.txid)
// .findFirst();
// // we don't need to check this but it saves a write tx instead of overwriting the transaction in Isar
// if (tx == null) {
await isar.writeTxn(() async {
await isar.transactions.put(txn.item1);
});
// }
txns.add(txn);
}
await addNewTransactionData(txns);
}
int estimateTxFee({required int vSize, required int feeRatePerKB}) {

View file

@ -3228,241 +3228,58 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
}
}
bool _duplicateTxCheck(
List<Map<String, dynamic>> allTransactions, String txid) {
for (int i = 0; i < allTransactions.length; i++) {
if (allTransactions[i]["txid"] == txid) {
return true;
}
}
return false;
}
Future<void> _refreshTransactions() async {
// final changeAddresses =
// DB.instance.get<dynamic>(boxName: walletId, key: 'changeAddresses')
// as List<dynamic>;
// final List<String> allAddresses = await _fetchAllOwnAddresses();
// // Logging.instance.log("receiving addresses: $receivingAddresses");
// // Logging.instance.log("change addresses: $changeAddresses");
//
// List<Map<String, dynamic>> allTxHashes = await _fetchHistory(allAddresses);
//
// final cachedTransactions =
// DB.instance.get<dynamic>(boxName: walletId, key: 'latest_tx_model')
// as models.TransactionData?;
// int latestTxnBlockHeight =
// DB.instance.get<dynamic>(boxName: walletId, key: "storedTxnDataHeight")
// as int? ??
// 0;
//
// final unconfirmedCachedTransactions =
// cachedTransactions?.getAllTransactions() ?? {};
// unconfirmedCachedTransactions
// .removeWhere((key, value) => value.confirmedStatus);
//
// if (cachedTransactions != null) {
// for (final tx in allTxHashes.toList(growable: false)) {
// final txHeight = tx["height"] as int;
// if (txHeight > 0 &&
// txHeight < latestTxnBlockHeight - MINIMUM_CONFIRMATIONS) {
// if (unconfirmedCachedTransactions[tx["tx_hash"] as String] == null) {
// allTxHashes.remove(tx);
// }
// }
// }
// }
//
// List<String> hashes = [];
// for (var element in allTxHashes) {
// hashes.add(element['tx_hash'] as String);
// }
final List<isar_models.Address> allAddresses =
await _fetchAllOwnAddresses();
final List<Map<String, dynamic>> allTxHashes =
await _fetchHistory(allAddresses.map((e) => e.value).toList());
List<String> hashes =
allTxHashes.map((e) => e['tx_hash'] as String).toList(growable: false);
List<Map<String, dynamic>> allTransactions = [];
List<Map<String, dynamic>> allTransactions = await fastFetch(hashes);
final currentHeight = await chainHeight;
Logging.instance.log("allTransactions length: ${allTransactions.length}",
level: LogLevel.Info);
for (final txHash in allTxHashes) {
final storedTx = await isar.transactions
.where()
.txidEqualTo(txHash["tx_hash"] as String)
.findFirst();
if (storedTx == null ||
!storedTx.isConfirmed(currentHeight, MINIMUM_CONFIRMATIONS)) {
final tx = await cachedElectrumXClient.getTransaction(
txHash: txHash["tx_hash"] as String,
verbose: true,
coin: coin,
);
if (!_duplicateTxCheck(allTransactions, tx["txid"] as String)) {
tx["address"] = await isar.addresses
.filter()
.valueEqualTo(txHash["address"] as String)
.findFirst();
tx["height"] = txHash["height"];
allTransactions.add(tx);
}
}
}
final List<
Tuple4<isar_models.Transaction, List<isar_models.Output>,
List<isar_models.Input>, isar_models.Address?>> txnsData = [];
// // sort thing stuff
// final currentPrice = await firoPrice;
// final List<Map<String, dynamic>> midSortedArray = [];
//
// final locale =
// Platform.isWindows ? "en_US" : await Devicelocale.currentLocale;
//
// Logging.instance.log("refresh the txs", level: LogLevel.Info);
for (final txObject in allTransactions) {
// // Logging.instance.log(txObject);
// List<String> sendersArray = [];
// List<String> recipientsArray = [];
//
// // Usually only has value when txType = 'Send'
// int inputAmtSentFromWallet = 0;
// // Usually has value regardless of txType due to change addresses
// int outputAmtAddressedToWallet = 0;
//
// Map<String, dynamic> midSortedTx = {};
// List<dynamic> aliens = [];
//
// for (final input in txObject["vin"] as List) {
// final address = input["address"] as String?;
// if (address != null) {
// sendersArray.add(address);
// }
// }
//
// // Logging.instance.log("sendersArray: $sendersArray");
//
// for (final output in txObject["vout"] as List) {
// final addresses = output["scriptPubKey"]["addresses"] as List?;
// if (addresses != null && addresses.isNotEmpty) {
// recipientsArray.add(addresses[0] as String);
// }
// }
// // Logging.instance.log("recipientsArray: $recipientsArray");
//
// final foundInSenders =
// allAddresses.any((element) => sendersArray.contains(element));
// // Logging.instance.log("foundInSenders: $foundInSenders");
//
// String outAddress = "";
//
// int fees = 0;
//
// // If txType = Sent, then calculate inputAmtSentFromWallet, calculate who received how much in aliens array (check outputs)
// if (foundInSenders) {
// int outAmount = 0;
// int inAmount = 0;
// bool nFeesUsed = false;
//
// for (final input in txObject["vin"] as List) {
// final nFees = input["nFees"];
// if (nFees != null) {
// nFeesUsed = true;
// fees = (Decimal.parse(nFees.toString()) *
// Decimal.fromInt(Constants.satsPerCoin(coin)))
// .toBigInt()
// .toInt();
// }
// final address = input["address"];
// final value = input["valueSat"];
// if (address != null && value != null) {
// if (allAddresses.contains(address)) {
// inputAmtSentFromWallet += value as int;
// }
// }
//
// if (value != null) {
// inAmount += value as int;
// }
// }
//
// for (final output in txObject["vout"] as List) {
// final addresses = output["scriptPubKey"]["addresses"] as List?;
// final value = output["value"];
// if (addresses != null && addresses.isNotEmpty) {
// final address = addresses[0] as String;
// if (value != null) {
// if (changeAddresses.contains(address)) {
// inputAmtSentFromWallet -= (Decimal.parse(value.toString()) *
// Decimal.fromInt(Constants.satsPerCoin(coin)))
// .toBigInt()
// .toInt();
// } else {
// outAddress = address;
// }
// }
// }
// if (value != null) {
// outAmount += (Decimal.parse(value.toString()) *
// Decimal.fromInt(Constants.satsPerCoin(coin)))
// .toBigInt()
// .toInt();
// }
// }
//
// fees = nFeesUsed ? fees : inAmount - outAmount;
// inputAmtSentFromWallet -= inAmount - outAmount;
// } else {
// for (final input in txObject["vin"] as List) {
// final nFees = input["nFees"];
// if (nFees != null) {
// fees += (Decimal.parse(nFees.toString()) *
// Decimal.fromInt(Constants.satsPerCoin(coin)))
// .toBigInt()
// .toInt();
// }
// }
//
// for (final output in txObject["vout"] as List) {
// final addresses = output["scriptPubKey"]["addresses"] as List?;
// if (addresses != null && addresses.isNotEmpty) {
// final address = addresses[0] as String;
// final value = output["value"];
// // Logging.instance.log(address + value.toString());
//
// if (allAddresses.contains(address)) {
// outputAmtAddressedToWallet += (Decimal.parse(value.toString()) *
// Decimal.fromInt(Constants.satsPerCoin(coin)))
// .toBigInt()
// .toInt();
// outAddress = address;
// }
// }
// }
// }
//
// final int confirms = txObject["confirmations"] as int? ?? 0;
//
// // create final tx map
// midSortedTx["txid"] = txObject["txid"];
// midSortedTx["confirmed_status"] = confirms >= MINIMUM_CONFIRMATIONS;
// midSortedTx["confirmations"] = confirms;
// midSortedTx["timestamp"] = txObject["blocktime"] ??
// (DateTime.now().millisecondsSinceEpoch ~/ 1000);
// if (foundInSenders) {
// midSortedTx["txType"] = "Sent";
// midSortedTx["amount"] = inputAmtSentFromWallet;
// final String worthNow = Format.localizedStringAsFixed(
// value: ((currentPrice * Decimal.fromInt(inputAmtSentFromWallet)) /
// Decimal.fromInt(Constants.satsPerCoin(coin)))
// .toDecimal(scaleOnInfinitePrecision: 2),
// decimalPlaces: 2,
// locale: locale!);
// midSortedTx["worthNow"] = worthNow;
// midSortedTx["worthAtBlockTimestamp"] = worthNow;
// if (txObject["vout"][0]["scriptPubKey"]["type"] == "lelantusmint") {
// midSortedTx["subType"] = "mint";
// }
// } else {
// midSortedTx["txType"] = "Received";
// midSortedTx["amount"] = outputAmtAddressedToWallet;
// final worthNow = Format.localizedStringAsFixed(
// value:
// ((currentPrice * Decimal.fromInt(outputAmtAddressedToWallet)) /
// Decimal.fromInt(Constants.satsPerCoin(coin)))
// .toDecimal(scaleOnInfinitePrecision: 2),
// decimalPlaces: 2,
// locale: locale!);
// midSortedTx["worthNow"] = worthNow;
// midSortedTx["worthAtBlockTimestamp"] = worthNow;
// }
// midSortedTx["aliens"] = aliens;
// midSortedTx["fees"] = fees;
// midSortedTx["address"] = outAddress;
// midSortedTx["inputSize"] = txObject["vin"].length;
// midSortedTx["outputSize"] = txObject["vout"].length;
// midSortedTx["inputs"] = txObject["vin"];
// midSortedTx["outputs"] = txObject["vout"];
//
// final int height = txObject["height"] as int? ?? 0;
// midSortedTx["height"] = height;
//
// if (height >= latestTxnBlockHeight) {
// latestTxnBlockHeight = height;
// }
//
// midSortedArray.add(midSortedTx);
final txn = await parseTransaction(
final data = await parseTransaction(
txObject,
cachedElectrumXClient,
allAddresses,
@ -3470,80 +3287,10 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
MINIMUM_CONFIRMATIONS,
);
// final tx = await isar.transactions
// .filter()
// .txidMatches(midSortedTx.txid)
// .findFirst();
// // we don't need to check this but it saves a write tx instead of overwriting the transaction in Isar
// if (tx == null) {
await isar.writeTxn(() async {
await isar.transactions.put(txn.item1);
});
// }
txnsData.add(data);
}
//
// // sort by date ---- //TODO not sure if needed
// // shouldn't be any issues with a null timestamp but I got one at some point?
// midSortedArray.sort((a, b) {
// final aT = a["timestamp"];
// final bT = b["timestamp"];
//
// if (aT == null && bT == null) {
// return 0;
// } else if (aT == null) {
// return -1;
// } else if (bT == null) {
// return 1;
// } else {
// return (bT as int) - (aT as int);
// }
// });
//
// // buildDateTimeChunks
// final Map<String, dynamic> result = {"dateTimeChunks": <dynamic>[]};
// final dateArray = <dynamic>[];
//
// for (int i = 0; i < midSortedArray.length; i++) {
// final txObject = midSortedArray[i];
// final date =
// models.extractDateFromTimestamp(txObject["timestamp"] as int);
// final txTimeArray = [txObject["timestamp"], date];
//
// if (dateArray.contains(txTimeArray[1])) {
// result["dateTimeChunks"].forEach((dynamic chunk) {
// if (models.extractDateFromTimestamp(chunk["timestamp"] as int) ==
// txTimeArray[1]) {
// if (chunk["transactions"] == null) {
// chunk["transactions"] = <Map<String, dynamic>>[];
// }
// chunk["transactions"].add(txObject);
// }
// });
// } else {
// dateArray.add(txTimeArray[1]);
// final chunk = {
// "timestamp": txTimeArray[0],
// "transactions": [txObject],
// };
// result["dateTimeChunks"].add(chunk);
// }
// }
//
// final transactionsMap = cachedTransactions?.getAllTransactions() ?? {};
// transactionsMap
// .addAll(models.TransactionData.fromJson(result).getAllTransactions());
//
// final txModel = models.TransactionData.fromMap(transactionsMap);
//
// await DB.instance.put<dynamic>(
// boxName: walletId,
// key: 'storedTxnDataHeight',
// value: latestTxnBlockHeight);
// await DB.instance.put<dynamic>(
// boxName: walletId, key: 'latest_tx_model', value: txModel);
//
// cachedTxData = txModel;
// return txModel;
await addNewTransactionData(txnsData);
}
Future<void> _refreshUTXOs() async {

View file

@ -2094,83 +2094,42 @@ class LitecoinWallet extends CoinServiceAPI with WalletCache, WalletDB {
final List<isar_models.Address> allAddresses =
await _fetchAllOwnAddresses();
// final changeAddresses = DB.instance.get<dynamic>(
// boxName: walletId, key: 'changeAddressesP2WPKH') as List<dynamic>;
// final changeAddressesP2PKH =
// DB.instance.get<dynamic>(boxName: walletId, key: 'changeAddressesP2PKH')
// as List<dynamic>;
// final changeAddressesP2SH =
// DB.instance.get<dynamic>(boxName: walletId, key: 'changeAddressesP2SH')
// as List<dynamic>;
//
// for (var i = 0; i < changeAddressesP2PKH.length; i++) {
// changeAddresses.add(changeAddressesP2PKH[i] as String);
// }
// for (var i = 0; i < changeAddressesP2SH.length; i++) {
// changeAddresses.add(changeAddressesP2SH[i] as String);
// }
final List<Map<String, dynamic>> allTxHashes =
await _fetchHistory(allAddresses.map((e) => e.value).toList());
// final cachedTransactions =
// DB.instance.get<dynamic>(boxName: walletId, key: 'latest_tx_model')
// as TransactionData?;
// int latestTxnBlockHeight =
// DB.instance.get<dynamic>(boxName: walletId, key: "storedTxnDataHeight")
// as int? ??
// 0;
//
// final unconfirmedCachedTransactions =
// cachedTransactions?.getAllTransactions() ?? {};
// unconfirmedCachedTransactions
// .removeWhere((key, value) => value.confirmedStatus);
//
// if (cachedTransactions != null) {
// for (final tx in allTxHashes.toList(growable: false)) {
// final txHeight = tx["height"] as int;
// if (txHeight > 0 &&
// txHeight < latestTxnBlockHeight - MINIMUM_CONFIRMATIONS) {
// if (unconfirmedCachedTransactions[tx["tx_hash"] as String] == null) {
// allTxHashes.remove(tx);
// }
// }
// }
// }
Set<String> hashes = {};
for (var element in allTxHashes) {
hashes.add(element['tx_hash'] as String);
}
await fastFetch(hashes.toList());
List<Map<String, dynamic>> allTransactions = [];
final currentHeight = await chainHeight;
for (final txHash in allTxHashes) {
final tx = await cachedElectrumXClient.getTransaction(
txHash: txHash["tx_hash"] as String,
verbose: true,
coin: coin,
);
final storedTx = await isar.transactions
.where()
.txidEqualTo(txHash["tx_hash"] as String)
.findFirst();
// Logging.instance.log("TRANSACTION: ${jsonEncode(tx)}");
if (!_duplicateTxCheck(allTransactions, tx["txid"] as String)) {
tx["address"] = txHash["address"];
tx["height"] = txHash["height"];
allTransactions.add(tx);
if (storedTx == null ||
!storedTx.isConfirmed(currentHeight, MINIMUM_CONFIRMATIONS)) {
final tx = await cachedElectrumXClient.getTransaction(
txHash: txHash["tx_hash"] as String,
verbose: true,
coin: coin,
);
// Logging.instance.log("TRANSACTION: ${jsonEncode(tx)}");
if (!_duplicateTxCheck(allTransactions, tx["txid"] as String)) {
tx["address"] = txHash["address"];
tx["height"] = txHash["height"];
allTransactions.add(tx);
}
}
}
// Logging.instance.log("addAddresses: $allAddresses", level: LogLevel.Info);
// Logging.instance.log("allTxHashes: $allTxHashes", level: LogLevel.Info);
//
// Logging.instance.log("allTransactions length: ${allTransactions.length}",
// level: LogLevel.Info);
//
// final priceData =
// await _priceAPI.getPricesAnd24hChange(baseCurrency: _prefs.currency);
// Decimal currentPrice = priceData[coin]?.item1 ?? Decimal.zero;
// final List<Map<String, dynamic>> midSortedArray = [];
// prefetch/cache
Set<String> vHashes = {};
for (final txObject in allTransactions) {
for (int i = 0; i < (txObject["vin"] as List).length; i++) {
@ -2181,8 +2140,12 @@ class LitecoinWallet extends CoinServiceAPI with WalletCache, WalletDB {
}
await fastFetch(vHashes.toList());
final List<
Tuple4<isar_models.Transaction, List<isar_models.Output>,
List<isar_models.Input>, isar_models.Address?>> txnsData = [];
for (final txObject in allTransactions) {
final txn = await parseTransaction(
final data = await parseTransaction(
txObject,
cachedElectrumXClient,
allAddresses,
@ -2190,261 +2153,9 @@ class LitecoinWallet extends CoinServiceAPI with WalletCache, WalletDB {
MINIMUM_CONFIRMATIONS,
);
// final tx = await isar.transactions
// .filter()
// .txidMatches(midSortedTx.txid)
// .findFirst();
// // we don't need to check this but it saves a write tx instead of overwriting the transaction in Isar
// if (tx == null) {
await isar.writeTxn(() async {
await isar.transactions.put(txn.item1);
});
// }
// List<String> sendersArray = [];
// List<String> recipientsArray = [];
//
// // Usually only has value when txType = 'Send'
// int inputAmtSentFromWallet = 0;
// // Usually has value regardless of txType due to change addresses
// int outputAmtAddressedToWallet = 0;
// int fee = 0;
//
// Map<String, dynamic> midSortedTx = {};
//
// for (int i = 0; i < (txObject["vin"] as List).length; i++) {
// final input = txObject["vin"]![i] as Map;
// final prevTxid = input["txid"] as String;
// final prevOut = input["vout"] as int;
//
// final tx = await _cachedElectrumXClient.getTransaction(
// txHash: prevTxid,
// coin: coin,
// );
//
// for (final out in tx["vout"] as List) {
// if (prevOut == out["n"]) {
// final address = out["scriptPubKey"]["addresses"][0] as String?;
// if (address != null) {
// sendersArray.add(address);
// }
// }
// }
// }
//
// Logging.instance.log("sendersArray: $sendersArray", level: LogLevel.Info);
//
// for (final output in txObject["vout"] as List) {
// final address = output["scriptPubKey"]["addresses"][0] as String?;
// if (address != null) {
// recipientsArray.add(address);
// }
// }
//
// Logging.instance
// .log("recipientsArray: $recipientsArray", level: LogLevel.Info);
//
// final foundInSenders =
// allAddresses.any((element) => sendersArray.contains(element));
// Logging.instance
// .log("foundInSenders: $foundInSenders", level: LogLevel.Info);
//
// // If txType = Sent, then calculate inputAmtSentFromWallet
// if (foundInSenders) {
// int totalInput = 0;
// for (int i = 0; i < (txObject["vin"] as List).length; i++) {
// final input = txObject["vin"]![i] as Map;
// final prevTxid = input["txid"] as String;
// final prevOut = input["vout"] as int;
// final tx = await _cachedElectrumXClient.getTransaction(
// txHash: prevTxid,
// coin: coin,
// );
//
// for (final out in tx["vout"] as List) {
// if (prevOut == out["n"]) {
// inputAmtSentFromWallet +=
// (Decimal.parse(out["value"]!.toString()) *
// Decimal.fromInt(Constants.satsPerCoin(coin)))
// .toBigInt()
// .toInt();
// }
// }
// }
// totalInput = inputAmtSentFromWallet;
// int totalOutput = 0;
//
// for (final output in txObject["vout"] as List) {
// final String address =
// output["scriptPubKey"]!["addresses"][0] as String;
// final value = output["value"]!;
// final _value = (Decimal.parse(value.toString()) *
// Decimal.fromInt(Constants.satsPerCoin(coin)))
// .toBigInt()
// .toInt();
// totalOutput += _value;
// if (changeAddresses.contains(address)) {
// inputAmtSentFromWallet -= _value;
// } else {
// // change address from 'sent from' to the 'sent to' address
// txObject["address"] = address;
// }
// }
// // calculate transaction fee
// fee = totalInput - totalOutput;
// // subtract fee from sent to calculate correct value of sent tx
// inputAmtSentFromWallet -= fee;
// } else {
// // counters for fee calculation
// int totalOut = 0;
// int totalIn = 0;
//
// // add up received tx value
// for (final output in txObject["vout"] as List) {
// final address = output["scriptPubKey"]["addresses"][0];
// if (address != null) {
// final value = (Decimal.parse(output["value"].toString()) *
// Decimal.fromInt(Constants.satsPerCoin(coin)))
// .toBigInt()
// .toInt();
// totalOut += value;
// if (allAddresses.contains(address)) {
// outputAmtAddressedToWallet += value;
// }
// }
// }
//
// // calculate fee for received tx
// for (int i = 0; i < (txObject["vin"] as List).length; i++) {
// final input = txObject["vin"][i] as Map;
// final prevTxid = input["txid"] as String;
// final prevOut = input["vout"] as int;
// final tx = await _cachedElectrumXClient.getTransaction(
// txHash: prevTxid,
// coin: coin,
// );
//
// for (final out in tx["vout"] as List) {
// if (prevOut == out["n"]) {
// totalIn += (Decimal.parse(out["value"].toString()) *
// Decimal.fromInt(Constants.satsPerCoin(coin)))
// .toBigInt()
// .toInt();
// }
// }
// }
// fee = totalIn - totalOut;
// }
//
// // create final tx map
// midSortedTx["txid"] = txObject["txid"];
// midSortedTx["confirmed_status"] = (txObject["confirmations"] != null) &&
// (txObject["confirmations"] as int >= MINIMUM_CONFIRMATIONS);
// midSortedTx["confirmations"] = txObject["confirmations"] ?? 0;
// midSortedTx["timestamp"] = txObject["blocktime"] ??
// (DateTime.now().millisecondsSinceEpoch ~/ 1000);
//
// if (foundInSenders) {
// midSortedTx["txType"] = "Sent";
// midSortedTx["amount"] = inputAmtSentFromWallet;
// final String worthNow =
// ((currentPrice * Decimal.fromInt(inputAmtSentFromWallet)) /
// Decimal.fromInt(Constants.satsPerCoin(coin)))
// .toDecimal(scaleOnInfinitePrecision: 2)
// .toStringAsFixed(2);
// midSortedTx["worthNow"] = worthNow;
// midSortedTx["worthAtBlockTimestamp"] = worthNow;
// } else {
// midSortedTx["txType"] = "Received";
// midSortedTx["amount"] = outputAmtAddressedToWallet;
// final worthNow =
// ((currentPrice * Decimal.fromInt(outputAmtAddressedToWallet)) /
// Decimal.fromInt(Constants.satsPerCoin(coin)))
// .toDecimal(scaleOnInfinitePrecision: 2)
// .toStringAsFixed(2);
// midSortedTx["worthNow"] = worthNow;
// }
// midSortedTx["aliens"] = <dynamic>[];
// midSortedTx["fees"] = fee;
// midSortedTx["address"] = txObject["address"];
// midSortedTx["inputSize"] = txObject["vin"].length;
// midSortedTx["outputSize"] = txObject["vout"].length;
// midSortedTx["inputs"] = txObject["vin"];
// midSortedTx["outputs"] = txObject["vout"];
//
// final int height = txObject["height"] as int;
// midSortedTx["height"] = height;
//
// if (height >= latestTxnBlockHeight) {
// latestTxnBlockHeight = height;
// }
//
// midSortedArray.add(midSortedTx);
txnsData.add(data);
}
//
// // sort by date ---- //TODO not sure if needed
// // shouldn't be any issues with a null timestamp but I got one at some point?
// midSortedArray
// .sort((a, b) => (b["timestamp"] as int) - (a["timestamp"] as int));
// // {
// // final aT = a["timestamp"];
// // final bT = b["timestamp"];
// //
// // if (aT == null && bT == null) {
// // return 0;
// // } else if (aT == null) {
// // return -1;
// // } else if (bT == null) {
// // return 1;
// // } else {
// // return bT - aT;
// // }
// // });
//
// // buildDateTimeChunks
// final Map<String, dynamic> result = {"dateTimeChunks": <dynamic>[]};
// final dateArray = <dynamic>[];
//
// for (int i = 0; i < midSortedArray.length; i++) {
// final txObject = midSortedArray[i];
// final date = extractDateFromTimestamp(txObject["timestamp"] as int);
// final txTimeArray = [txObject["timestamp"], date];
//
// if (dateArray.contains(txTimeArray[1])) {
// result["dateTimeChunks"].forEach((dynamic chunk) {
// if (extractDateFromTimestamp(chunk["timestamp"] as int) ==
// txTimeArray[1]) {
// if (chunk["transactions"] == null) {
// chunk["transactions"] = <Map<String, dynamic>>[];
// }
// chunk["transactions"].add(txObject);
// }
// });
// } else {
// dateArray.add(txTimeArray[1]);
// final chunk = {
// "timestamp": txTimeArray[0],
// "transactions": [txObject],
// };
// result["dateTimeChunks"].add(chunk);
// }
// }
//
// final transactionsMap = cachedTransactions?.getAllTransactions() ?? {};
// transactionsMap
// .addAll(TransactionData.fromJson(result).getAllTransactions());
//
// final txModel = TransactionData.fromMap(transactionsMap);
//
// await DB.instance.put<dynamic>(
// boxName: walletId,
// key: 'storedTxnDataHeight',
// value: latestTxnBlockHeight);
// await DB.instance.put<dynamic>(
// boxName: walletId, key: 'latest_tx_model', value: txModel);
//
// cachedTxData = txModel;
// return txModel;
await addNewTransactionData(txnsData);
}
int estimateTxFee({required int vSize, required int feeRatePerKB}) {

View file

@ -46,6 +46,7 @@ import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/prefs.dart';
import 'package:stackwallet/utilities/stack_file_system.dart';
import 'package:tuple/tuple.dart';
const int MINIMUM_CONFIRMATIONS = 10;
@ -848,7 +849,9 @@ class MoneroWallet extends CoinServiceAPI with WalletCache, WalletDB {
//
// final Set<String> cachedTxids = Set<String>.from(txidsList);
final List<isar_models.Transaction> txns = [];
final List<
Tuple4<isar_models.Transaction, List<isar_models.Output>,
List<isar_models.Input>, isar_models.Address?>> txnsData = [];
if (transactions != null) {
for (var tx in transactions.entries) {
@ -865,6 +868,7 @@ class MoneroWallet extends CoinServiceAPI with WalletCache, WalletDB {
txn.txid = tx.value.id;
txn.timestamp = (tx.value.date.millisecondsSinceEpoch ~/ 1000);
isar_models.Address? address;
if (tx.value.direction == TransactionDirection.incoming) {
final addressInfo = tx.value.additionalInfo;
@ -874,7 +878,7 @@ class MoneroWallet extends CoinServiceAPI with WalletCache, WalletDB {
);
if (addressString != null) {
txn.address.value = await isar.addresses
address = await isar.addresses
.filter()
.valueEqualTo(addressString)
.findFirst();
@ -899,67 +903,11 @@ class MoneroWallet extends CoinServiceAPI with WalletCache, WalletDB {
txn.slateId = null;
txn.otherData = null;
txns.add(txn);
txnsData.add(Tuple4(txn, [], [], address));
}
}
await isar.writeTxn(() async {
await isar.transactions.putAll(txns);
});
// // sort by date ----
// midSortedArray
// .sort((a, b) => (b["timestamp"] as int) - (a["timestamp"] as int));
// Logging.instance.log(midSortedArray, level: LogLevel.Info);
//
// // buildDateTimeChunks
// final Map<String, dynamic> result = {"dateTimeChunks": <dynamic>[]};
// final dateArray = <dynamic>[];
//
// for (int i = 0; i < midSortedArray.length; i++) {
// final txObject = midSortedArray[i];
// final date = extractDateFromTimestamp(txObject["timestamp"] as int);
// final txTimeArray = [txObject["timestamp"], date];
//
// if (dateArray.contains(txTimeArray[1])) {
// result["dateTimeChunks"].forEach((dynamic chunk) {
// if (extractDateFromTimestamp(chunk["timestamp"] as int) ==
// txTimeArray[1]) {
// if (chunk["transactions"] == null) {
// chunk["transactions"] = <Map<String, dynamic>>[];
// }
// chunk["transactions"].add(txObject);
// }
// });
// } else {
// dateArray.add(txTimeArray[1]);
// final chunk = {
// "timestamp": txTimeArray[0],
// "transactions": [txObject],
// };
// result["dateTimeChunks"].add(chunk);
// }
// }
//
// // final transactionsMap = cachedTransactions?.getAllTransactions() ?? {};
// final Map<String, Transaction> transactionsMap = {};
// transactionsMap
// .addAll(TransactionData.fromJson(result).getAllTransactions());
//
// final txModel = TransactionData.fromMap(transactionsMap);
//
// // await DB.instance.put<dynamic>(
// // boxName: walletId,
// // key: 'storedTxnDataHeight',
// // value: latestTxnBlockHeight);
// // await DB.instance.put<dynamic>(
// // boxName: walletId, key: 'latest_tx_model', value: txModel);
// // await DB.instance.put<dynamic>(
// // boxName: walletId,
// // key: 'cachedTxids',
// // value: cachedTxids.toList(growable: false));
//
// return txModel;
await addNewTransactionData(txnsData);
}
Future<String> _pathForWalletDir({

View file

@ -2076,69 +2076,37 @@ class NamecoinWallet extends CoinServiceAPI with WalletCache, WalletDB {
final List<isar_models.Address> allAddresses =
await _fetchAllOwnAddresses();
// final changeAddresses = DB.instance.get<dynamic>(
// boxName: walletId, key: 'changeAddressesP2WPKH') as List<dynamic>;
// final changeAddressesP2PKH =
// DB.instance.get<dynamic>(boxName: walletId, key: 'changeAddressesP2PKH')
// as List<dynamic>;
// final changeAddressesP2SH =
// DB.instance.get<dynamic>(boxName: walletId, key: 'changeAddressesP2SH')
// as List<dynamic>;
//
// for (var i = 0; i < changeAddressesP2PKH.length; i++) {
// changeAddresses.add(changeAddressesP2PKH[i] as String);
// }
// for (var i = 0; i < changeAddressesP2SH.length; i++) {
// changeAddresses.add(changeAddressesP2SH[i] as String);
// }
final List<Map<String, dynamic>> allTxHashes =
await _fetchHistory(allAddresses.map((e) => e.value).toList());
// final cachedTransactions =
// DB.instance.get<dynamic>(boxName: walletId, key: 'latest_tx_model')
// as TransactionData?;
// int latestTxnBlockHeight =
// DB.instance.get<dynamic>(boxName: walletId, key: "storedTxnDataHeight")
// as int? ??
// 0;
//
// final unconfirmedCachedTransactions =
// cachedTransactions?.getAllTransactions() ?? {};
// unconfirmedCachedTransactions
// .removeWhere((key, value) => value.confirmedStatus);
//
// if (cachedTransactions != null) {
// for (final tx in allTxHashes.toList(growable: false)) {
// final txHeight = tx["height"] as int;
// if (txHeight > 0 &&
// txHeight < latestTxnBlockHeight - MINIMUM_CONFIRMATIONS) {
// if (unconfirmedCachedTransactions[tx["tx_hash"] as String] == null) {
// allTxHashes.remove(tx);
// }
// }
// }
// }
Set<String> hashes = {};
for (var element in allTxHashes) {
hashes.add(element['tx_hash'] as String);
}
await fastFetch(hashes.toList());
List<Map<String, dynamic>> allTransactions = [];
final currentHeight = await chainHeight;
for (final txHash in allTxHashes) {
final tx = await cachedElectrumXClient.getTransaction(
txHash: txHash["tx_hash"] as String,
verbose: true,
coin: coin,
);
final storedTx = await isar.transactions
.where()
.txidEqualTo(txHash["tx_hash"] as String)
.findFirst();
// Logging.instance.log("TRANSACTION: ${jsonEncode(tx)}");
if (!_duplicateTxCheck(allTransactions, tx["txid"] as String)) {
tx["address"] = txHash["address"];
tx["height"] = txHash["height"];
allTransactions.add(tx);
if (storedTx == null ||
!storedTx.isConfirmed(currentHeight, MINIMUM_CONFIRMATIONS)) {
final tx = await cachedElectrumXClient.getTransaction(
txHash: txHash["tx_hash"] as String,
verbose: true,
coin: coin,
);
// Logging.instance.log("TRANSACTION: ${jsonEncode(tx)}");
if (!_duplicateTxCheck(allTransactions, tx["txid"] as String)) {
tx["address"] = txHash["address"];
tx["height"] = txHash["height"];
allTransactions.add(tx);
}
}
}
@ -2158,8 +2126,12 @@ class NamecoinWallet extends CoinServiceAPI with WalletCache, WalletDB {
}
await fastFetch(vHashes.toList());
final List<
Tuple4<isar_models.Transaction, List<isar_models.Output>,
List<isar_models.Input>, isar_models.Address>> txnsData = [];
for (final txObject in allTransactions) {
final txn = await parseTransaction(
final data = await parseTransaction(
txObject,
cachedElectrumXClient,
allAddresses,
@ -2167,271 +2139,9 @@ class NamecoinWallet extends CoinServiceAPI with WalletCache, WalletDB {
MINIMUM_CONFIRMATIONS,
);
// final tx = await isar.transactions
// .filter()
// .txidMatches(midSortedTx.txid)
// .findFirst();
// // we don't need to check this but it saves a write tx instead of overwriting the transaction in Isar
// if (tx == null) {
await isar.writeTxn(() async {
await isar.transactions.put(txn.item1);
});
// }
// List<String> sendersArray = [];
// List<String> recipientsArray = [];
//
// // Usually only has value when txType = 'Send'
// int inputAmtSentFromWallet = 0;
// // Usually has value regardless of txType due to change addresses
// int outputAmtAddressedToWallet = 0;
// int fee = 0;
//
// Map<String, dynamic> midSortedTx = {};
//
// for (int i = 0; i < (txObject["vin"] as List).length; i++) {
// final input = txObject["vin"]![i] as Map;
// final prevTxid = input["txid"] as String;
// final prevOut = input["vout"] as int;
//
// final tx = await _cachedElectrumXClient.getTransaction(
// txHash: prevTxid,
// coin: coin,
// );
//
// for (final out in tx["vout"] as List) {
// if (prevOut == out["n"]) {
// String? address = out["scriptPubKey"]["address"] as String?;
// if (address == null && out["scriptPubKey"]["address"] != null) {
// address = out["scriptPubKey"]["address"] as String?;
// }
//
// if (address != null) {
// sendersArray.add(address);
// }
// }
// }
// }
//
// Logging.instance.log("sendersArray: $sendersArray", level: LogLevel.Info);
//
// for (final output in txObject["vout"] as List) {
// String? address = output["scriptPubKey"]["address"] as String?;
// if (address == null && output["scriptPubKey"]["address"] != null) {
// address = output["scriptPubKey"]["address"] as String?;
// }
// if (address != null) {
// recipientsArray.add(address);
// }
// }
//
// Logging.instance
// .log("recipientsArray: $recipientsArray", level: LogLevel.Info);
//
// final foundInSenders =
// allAddresses.any((element) => sendersArray.contains(element));
// Logging.instance
// .log("foundInSenders: $foundInSenders", level: LogLevel.Info);
//
// // If txType = Sent, then calculate inputAmtSentFromWallet
// if (foundInSenders) {
// int totalInput = 0;
// for (int i = 0; i < (txObject["vin"] as List).length; i++) {
// final input = txObject["vin"]![i] as Map;
// final prevTxid = input["txid"] as String;
// final prevOut = input["vout"] as int;
// final tx = await _cachedElectrumXClient.getTransaction(
// txHash: prevTxid,
// coin: coin,
// );
//
// for (final out in tx["vout"] as List) {
// if (prevOut == out["n"]) {
// inputAmtSentFromWallet +=
// (Decimal.parse(out["value"]!.toString()) *
// Decimal.fromInt(Constants.satsPerCoin(coin)))
// .toBigInt()
// .toInt();
// }
// }
// }
// totalInput = inputAmtSentFromWallet;
// int totalOutput = 0;
//
// for (final output in txObject["vout"] as List) {
// Logging.instance.log(output, level: LogLevel.Info);
// final address = output["scriptPubKey"]["address"];
// final value = output["value"];
// final _value = (Decimal.parse(value.toString()) *
// Decimal.fromInt(Constants.satsPerCoin(coin)))
// .toBigInt()
// .toInt();
// totalOutput += _value;
// if (changeAddresses.contains(address)) {
// inputAmtSentFromWallet -= _value;
// } else {
// // change address from 'sent from' to the 'sent to' address
// txObject["address"] = address;
// }
// }
// // calculate transaction fee
// fee = totalInput - totalOutput;
// // subtract fee from sent to calculate correct value of sent tx
// inputAmtSentFromWallet -= fee;
// } else {
// // counters for fee calculation
// int totalOut = 0;
// int totalIn = 0;
//
// // add up received tx value
// for (final output in txObject["vout"] as List) {
// String? address = output["scriptPubKey"]["address"] as String?;
// if (address == null && output["scriptPubKey"]["address"] != null) {
// address = output["scriptPubKey"]["address"] as String?;
// }
// if (address != null) {
// final value = (Decimal.parse(output["value"].toString()) *
// Decimal.fromInt(Constants.satsPerCoin(coin)))
// .toBigInt()
// .toInt();
// totalOut += value;
// if (allAddresses.contains(address)) {
// outputAmtAddressedToWallet += value;
// }
// }
// }
//
// // calculate fee for received tx
// for (int i = 0; i < (txObject["vin"] as List).length; i++) {
// final input = txObject["vin"][i] as Map;
// final prevTxid = input["txid"] as String;
// final prevOut = input["vout"] as int;
// final tx = await _cachedElectrumXClient.getTransaction(
// txHash: prevTxid,
// coin: coin,
// );
//
// for (final out in tx["vout"] as List) {
// if (prevOut == out["n"]) {
// totalIn += (Decimal.parse(out["value"].toString()) *
// Decimal.fromInt(Constants.satsPerCoin(coin)))
// .toBigInt()
// .toInt();
// }
// }
// }
// fee = totalIn - totalOut;
// }
//
// // create final tx map
// midSortedTx["txid"] = txObject["txid"];
// midSortedTx["confirmed_status"] = (txObject["confirmations"] != null) &&
// (txObject["confirmations"] as int >= MINIMUM_CONFIRMATIONS);
// midSortedTx["confirmations"] = txObject["confirmations"] ?? 0;
// midSortedTx["timestamp"] = txObject["blocktime"] ??
// (DateTime.now().millisecondsSinceEpoch ~/ 1000);
//
// if (foundInSenders) {
// midSortedTx["txType"] = "Sent";
// midSortedTx["amount"] = inputAmtSentFromWallet;
// final String worthNow =
// ((currentPrice * Decimal.fromInt(inputAmtSentFromWallet)) /
// Decimal.fromInt(Constants.satsPerCoin(coin)))
// .toDecimal(scaleOnInfinitePrecision: 2)
// .toStringAsFixed(2);
// midSortedTx["worthNow"] = worthNow;
// midSortedTx["worthAtBlockTimestamp"] = worthNow;
// } else {
// midSortedTx["txType"] = "Received";
// midSortedTx["amount"] = outputAmtAddressedToWallet;
// final worthNow =
// ((currentPrice * Decimal.fromInt(outputAmtAddressedToWallet)) /
// Decimal.fromInt(Constants.satsPerCoin(coin)))
// .toDecimal(scaleOnInfinitePrecision: 2)
// .toStringAsFixed(2);
// midSortedTx["worthNow"] = worthNow;
// }
// midSortedTx["aliens"] = <dynamic>[];
// midSortedTx["fees"] = fee;
// midSortedTx["address"] = txObject["address"];
// midSortedTx["inputSize"] = txObject["vin"].length;
// midSortedTx["outputSize"] = txObject["vout"].length;
// midSortedTx["inputs"] = txObject["vin"];
// midSortedTx["outputs"] = txObject["vout"];
//
// final int height = txObject["height"] as int;
// midSortedTx["height"] = height;
//
// if (height >= latestTxnBlockHeight) {
// latestTxnBlockHeight = height;
// }
//
// midSortedArray.add(midSortedTx);
txnsData.add(data);
}
//
// // sort by date ---- //TODO not sure if needed
// // shouldn't be any issues with a null timestamp but I got one at some point?
// midSortedArray
// .sort((a, b) => (b["timestamp"] as int) - (a["timestamp"] as int));
// // {
// // final aT = a["timestamp"];
// // final bT = b["timestamp"];
// //
// // if (aT == null && bT == null) {
// // return 0;
// // } else if (aT == null) {
// // return -1;
// // } else if (bT == null) {
// // return 1;
// // } else {
// // return bT - aT;
// // }
// // });
//
// // buildDateTimeChunks
// final Map<String, dynamic> result = {"dateTimeChunks": <dynamic>[]};
// final dateArray = <dynamic>[];
//
// for (int i = 0; i < midSortedArray.length; i++) {
// final txObject = midSortedArray[i];
// final date = extractDateFromTimestamp(txObject["timestamp"] as int);
// final txTimeArray = [txObject["timestamp"], date];
//
// if (dateArray.contains(txTimeArray[1])) {
// result["dateTimeChunks"].forEach((dynamic chunk) {
// if (extractDateFromTimestamp(chunk["timestamp"] as int) ==
// txTimeArray[1]) {
// if (chunk["transactions"] == null) {
// chunk["transactions"] = <Map<String, dynamic>>[];
// }
// chunk["transactions"].add(txObject);
// }
// });
// } else {
// dateArray.add(txTimeArray[1]);
// final chunk = {
// "timestamp": txTimeArray[0],
// "transactions": [txObject],
// };
// result["dateTimeChunks"].add(chunk);
// }
// }
//
// final transactionsMap = cachedTransactions?.getAllTransactions() ?? {};
// transactionsMap
// .addAll(TransactionData.fromJson(result).getAllTransactions());
//
// final txModel = TransactionData.fromMap(transactionsMap);
//
// await DB.instance.put<dynamic>(
// boxName: walletId,
// key: 'storedTxnDataHeight',
// value: latestTxnBlockHeight);
// await DB.instance.put<dynamic>(
// boxName: walletId, key: 'latest_tx_model', value: txModel);
//
// cachedTxData = txModel;
// return txModel;
await addNewTransactionData(txnsData);
}
int estimateTxFee({required int vSize, required int feeRatePerKB}) {

View file

@ -1962,9 +1962,6 @@ class ParticlWallet extends CoinServiceAPI with WalletCache, WalletDB {
Future<void> _refreshTransactions() async {
final allAddresses = await _fetchAllOwnAddresses();
// final changeAddresses = DB.instance.get<dynamic>(
// boxName: walletId, key: 'changeAddressesP2WPKH') as List<dynamic>;
List<String> changeAddresses = allAddresses
.where((e) => e.subType == isar_models.AddressSubType.change)
.map((e) => e.value)
@ -1973,54 +1970,38 @@ class ParticlWallet extends CoinServiceAPI with WalletCache, WalletDB {
final List<Map<String, dynamic>> allTxHashes = await _fetchHistory(
allAddresses.map((e) => e.value).toList(growable: false));
// final cachedTransactions =
// DB.instance.get<dynamic>(boxName: walletId, key: 'latest_tx_model')
// as TransactionData?;
// int latestTxnBlockHeight =
// DB.instance.get<dynamic>(boxName: walletId, key: "storedTxnDataHeight")
// as int? ??
// 0;
//
// final unconfirmedCachedTransactions =
// cachedTransactions?.getAllTransactions() ?? {};
// unconfirmedCachedTransactions
// .removeWhere((key, value) => value.confirmedStatus);
//
// if (cachedTransactions != null) {
// for (final tx in allTxHashes.toList(growable: false)) {
// final txHeight = tx["height"] as int;
// if (txHeight > 0 &&
// txHeight < latestTxnBlockHeight - MINIMUM_CONFIRMATIONS) {
// if (unconfirmedCachedTransactions[tx["tx_hash"] as String] == null) {
// allTxHashes.remove(tx);
// }
// }
// }
// }
Set<String> hashes = {};
for (var element in allTxHashes) {
hashes.add(element['tx_hash'] as String);
}
await fastFetch(hashes.toList());
List<Map<String, dynamic>> allTransactions = [];
final currentHeight = await chainHeight;
for (final txHash in allTxHashes) {
final tx = await cachedElectrumXClient.getTransaction(
txHash: txHash["tx_hash"] as String,
verbose: true,
coin: coin,
);
final storedTx = await isar.transactions
.where()
.txidEqualTo(txHash["tx_hash"] as String)
.findFirst();
// Logging.instance.log("TRANSACTION: ${jsonEncode(tx)}");
// TODO fix this for sent to self transactions?
if (!_duplicateTxCheck(allTransactions, tx["txid"] as String)) {
tx["address"] = await isar.addresses
.filter()
.valueEqualTo(txHash["address"] as String)
.findFirst();
tx["height"] = txHash["height"];
allTransactions.add(tx);
if (storedTx == null ||
!storedTx.isConfirmed(currentHeight, MINIMUM_CONFIRMATIONS)) {
final tx = await cachedElectrumXClient.getTransaction(
txHash: txHash["tx_hash"] as String,
verbose: true,
coin: coin,
);
// Logging.instance.log("TRANSACTION: ${jsonEncode(tx)}");
// TODO fix this for sent to self transactions?
if (!_duplicateTxCheck(allTransactions, tx["txid"] as String)) {
tx["address"] = await isar.addresses
.filter()
.valueEqualTo(txHash["address"] as String)
.findFirst();
tx["height"] = txHash["height"];
allTransactions.add(tx);
}
}
}
@ -2044,7 +2025,9 @@ class ParticlWallet extends CoinServiceAPI with WalletCache, WalletDB {
}
await fastFetch(vHashes.toList());
final List<isar_models.Transaction> txns = [];
final List<
Tuple4<isar_models.Transaction, List<isar_models.Output>,
List<isar_models.Input>, isar_models.Address?>> txns = [];
for (final txObject in allTransactions) {
List<String> sendersArray = [];
@ -2269,7 +2252,11 @@ class ParticlWallet extends CoinServiceAPI with WalletCache, WalletDB {
tx.subType = isar_models.TransactionSubType.none;
tx.fee = fee;
tx.address.value = midSortedTx["address"] as isar_models.Address?;
isar_models.Address? transactionAddress =
midSortedTx["address"] as isar_models.Address?;
List<isar_models.Input> inputs = [];
List<isar_models.Output> outputs = [];
for (final json in midSortedTx["vin"] as List) {
bool isCoinBase = json['coinbase'] != null;
@ -2282,7 +2269,7 @@ class ParticlWallet extends CoinServiceAPI with WalletCache, WalletDB {
isCoinBase ? isCoinBase : json['is_coinbase'] as bool?;
input.sequence = json['sequence'] as int?;
input.innerRedeemScriptAsm = json['innerRedeemscriptAsm'] as String?;
tx.inputs.add(input);
inputs.add(input);
}
for (final json in midSortedTx["vout"] as List) {
@ -2298,7 +2285,7 @@ class ParticlWallet extends CoinServiceAPI with WalletCache, WalletDB {
Decimal.tryParse(json["value"].toString()) ?? Decimal.zero,
coin,
);
tx.outputs.add(output);
outputs.add(output);
}
tx.height = height;
@ -2307,76 +2294,10 @@ class ParticlWallet extends CoinServiceAPI with WalletCache, WalletDB {
tx.slateId = null;
tx.otherData = null;
txns.add(tx);
txns.add(Tuple4(tx, outputs, inputs, transactionAddress));
}
await isar.writeTxn(() async {
await isar.transactions.putAll(txns);
});
//
// // sort by date ---- //TODO not sure if needed
// // shouldn't be any issues with a null timestamp but I got one at some point?
// midSortedArray
// .sort((a, b) => (b["timestamp"] as int) - (a["timestamp"] as int));
// // {
// // final aT = a["timestamp"];
// // final bT = b["timestamp"];
// //
// // if (aT == null && bT == null) {
// // return 0;
// // } else if (aT == null) {
// // return -1;
// // } else if (bT == null) {
// // return 1;
// // } else {
// // return bT - aT;
// // }
// // });
//
// // buildDateTimeChunks
// final Map<String, dynamic> result = {"dateTimeChunks": <dynamic>[]};
// final dateArray = <dynamic>[];
//
// for (int i = 0; i < midSortedArray.length; i++) {
// final txObject = midSortedArray[i];
// final date = extractDateFromTimestamp(txObject["timestamp"] as int);
// final txTimeArray = [txObject["timestamp"], date];
//
// if (dateArray.contains(txTimeArray[1])) {
// result["dateTimeChunks"].forEach((dynamic chunk) {
// if (extractDateFromTimestamp(chunk["timestamp"] as int) ==
// txTimeArray[1]) {
// if (chunk["transactions"] == null) {
// chunk["transactions"] = <Map<String, dynamic>>[];
// }
// chunk["transactions"].add(txObject);
// }
// });
// } else {
// dateArray.add(txTimeArray[1]);
// final chunk = {
// "timestamp": txTimeArray[0],
// "transactions": [txObject],
// };
// result["dateTimeChunks"].add(chunk);
// }
// }
//
// final transactionsMap = cachedTransactions?.getAllTransactions() ?? {};
// transactionsMap
// .addAll(TransactionData.fromJson(result).getAllTransactions());
//
// final txModel = TransactionData.fromMap(transactionsMap);
//
// await DB.instance.put<dynamic>(
// boxName: walletId,
// key: 'storedTxnDataHeight',
// value: latestTxnBlockHeight);
// await DB.instance.put<dynamic>(
// boxName: walletId, key: 'latest_tx_model', value: txModel);
//
// return txModel;
await addNewTransactionData(txns);
}
int estimateTxFee({required int vSize, required int feeRatePerKB}) {

View file

@ -48,6 +48,7 @@ import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/prefs.dart';
import 'package:stackwallet/utilities/stack_file_system.dart';
import 'package:tuple/tuple.dart';
const int MINIMUM_CONFIRMATIONS = 10;
@ -875,7 +876,9 @@ class WowneroWallet extends CoinServiceAPI with WalletCache, WalletDB {
// }
// }
final List<isar_models.Transaction> txns = [];
final List<
Tuple4<isar_models.Transaction, List<isar_models.Output>,
List<isar_models.Input>, isar_models.Address?>> txnsData = [];
if (transactions != null) {
for (var tx in transactions.entries) {
@ -934,6 +937,7 @@ class WowneroWallet extends CoinServiceAPI with WalletCache, WalletDB {
txn.txid = tx.value.id;
txn.timestamp = (tx.value.date.millisecondsSinceEpoch ~/ 1000);
isar_models.Address? address;
if (tx.value.direction == TransactionDirection.incoming) {
final addressInfo = tx.value.additionalInfo;
@ -943,7 +947,7 @@ class WowneroWallet extends CoinServiceAPI with WalletCache, WalletDB {
);
if (addressString != null) {
txn.address.value = await isar.addresses
address = await isar.addresses
.filter()
.valueEqualTo(addressString)
.findFirst();
@ -968,67 +972,11 @@ class WowneroWallet extends CoinServiceAPI with WalletCache, WalletDB {
txn.slateId = null;
txn.otherData = null;
txns.add(txn);
txnsData.add(Tuple4(txn, [], [], address));
}
}
await isar.writeTxn(() async {
await isar.transactions.putAll(txns);
});
// // sort by date ----
// midSortedArray
// .sort((a, b) => (b["timestamp"] as int) - (a["timestamp"] as int));
// Logging.instance.log(midSortedArray, level: LogLevel.Info);
//
// // buildDateTimeChunks
// final Map<String, dynamic> result = {"dateTimeChunks": <dynamic>[]};
// final dateArray = <dynamic>[];
//
// for (int i = 0; i < midSortedArray.length; i++) {
// final txObject = midSortedArray[i];
// final date = extractDateFromTimestamp(txObject["timestamp"] as int);
// final txTimeArray = [txObject["timestamp"], date];
//
// if (dateArray.contains(txTimeArray[1])) {
// result["dateTimeChunks"].forEach((dynamic chunk) {
// if (extractDateFromTimestamp(chunk["timestamp"] as int) ==
// txTimeArray[1]) {
// if (chunk["transactions"] == null) {
// chunk["transactions"] = <Map<String, dynamic>>[];
// }
// chunk["transactions"].add(txObject);
// }
// });
// } else {
// dateArray.add(txTimeArray[1]);
// final chunk = {
// "timestamp": txTimeArray[0],
// "transactions": [txObject],
// };
// result["dateTimeChunks"].add(chunk);
// }
// }
//
// // final transactionsMap = cachedTransactions?.getAllTransactions() ?? {};
// final Map<String, Transaction> transactionsMap = {};
// transactionsMap
// .addAll(TransactionData.fromJson(result).getAllTransactions());
//
// final txModel = TransactionData.fromMap(transactionsMap);
// //
// // await DB.instance.put<dynamic>(
// // boxName: walletId,
// // key: 'storedTxnDataHeight',
// // value: latestTxnBlockHeight);
// // await DB.instance.put<dynamic>(
// // boxName: walletId, key: 'latest_tx_model', value: txModel);
// // await DB.instance.put<dynamic>(
// // boxName: walletId,
// // key: 'cachedTxids',
// // value: cachedTxids.toList(growable: false));
//
// return txModel;
await addNewTransactionData(txnsData);
}
Future<String> _pathForWalletDir({

View file

@ -1,6 +1,7 @@
import 'package:isar/isar.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/utilities/stack_file_system.dart';
import 'package:tuple/tuple.dart';
mixin WalletDB {
Isar? _isar;
@ -29,4 +30,45 @@ mixin WalletDB {
}
Future<bool> isarClose() async => await _isar?.close() ?? false;
Future<void> addNewTransactionData(
List<Tuple4<Transaction, List<Output>, List<Input>, Address?>>
transactionsData) async {
await isar.writeTxn(() async {
for (final data in transactionsData) {
final tx = data.item1;
// save transaction
await isar.transactions.put(tx);
// link and save outputs
if (data.item2.isNotEmpty) {
await isar.outputs.putAll(data.item2);
tx.outputs.addAll(data.item2);
await tx.outputs.save();
}
// link and save inputs
if (data.item3.isNotEmpty) {
await isar.inputs.putAll(data.item3);
tx.inputs.addAll(data.item3);
await tx.inputs.save();
}
if (data.item4 != null) {
// check if address exists in db and add if it does not
if (await isar.addresses
.where()
.valueEqualTo(data.item4!.value)
.findFirst() ==
null) {
await isar.addresses.put(data.item4!);
}
// link and save address
tx.address.value = data.item4;
await tx.address.save();
}
}
});
}
}