diff --git a/lib/services/csv_exporter.dart b/lib/services/csv_exporter.dart new file mode 100644 index 000000000..8c08e5d18 --- /dev/null +++ b/lib/services/csv_exporter.dart @@ -0,0 +1,152 @@ +import 'package:csv/csv.dart'; + +import '../models/isar/models/blockchain_data/v2/transaction_v2.dart'; +import '../models/isar/models/isar_models.dart'; +import '../utilities/amount/amount.dart'; +import '../utilities/amount/amount_formatter.dart'; +import '../utilities/amount/amount_unit.dart'; +import '../wallets/crypto_currency/crypto_currency.dart'; + +abstract final class CsvExporter { + String transactionV2sToCsv( + List transactions, + List notes, + CryptoCurrency coin, + String locale, + EthContract? ethContract, + String? sparkChangeAddress, + ) { + final List?> rows = []; + + rows.add([ + "timestamp", + "height", + "txid", + "amount", + "fee", + "type", + "sub type", + "note", + ]); + + for (final _transaction in transactions) { + final amountFormatter = AmountFormatter( + unit: AmountUnit.normal, + locale: locale, + coin: coin, + maxDecimals: coin.fractionDigits, + ); + final List row = []; + + // unix timestamp + row.add(_transaction.timestamp.toString()); + + // height + row.add(_transaction.height.toString()); + + // txid + row.add(_transaction.txid); + + // amount + row.add( + _parseAmountFromTxnV2( + _transaction, + amountFormatter, + ethContract, + sparkChangeAddress, + ), + ); + + // fee + row.add( + amountFormatter.format( + _transaction.getFee( + fractionDigits: amountFormatter.coin.fractionDigits), + ethContract: ethContract, + withUnitName: false, + ), + ); + + // type + row.add(_transaction.type.name); + + // sub type + row.add(_transaction.subType.name); + + // note + final note = notes.firstWhere( + (e) => e.txid == _transaction.txid, + orElse: () => TransactionNote(walletId: "", txid: "", value: ""), + ); + row.add(note.value); + + // finally add row + rows.add(row); + } + + // convert + final csv = const ListToCsvConverter().convert(rows); + + return csv; + } + + String _parseAmountFromTxnV2( + TransactionV2 txn, + AmountFormatter amountFormatter, + EthContract? ethContract, + String? sparkChangeAddress, + ) { + final Amount amount; + final fractionDigits = + ethContract?.decimals ?? amountFormatter.coin.fractionDigits; + if (txn.subType == TransactionSubType.cashFusion) { + amount = txn.getAmountReceivedInThisWallet( + fractionDigits: fractionDigits, + ); + } else { + switch (txn.type) { + case TransactionType.outgoing: + amount = txn.getAmountSentFromThisWallet( + fractionDigits: fractionDigits, + ); + break; + + case TransactionType.incoming: + case TransactionType.sentToSelf: + if (txn.subType == TransactionSubType.sparkMint) { + amount = txn.getAmountSparkSelfMinted( + fractionDigits: fractionDigits, + ); + } else if (txn.subType == TransactionSubType.sparkSpend) { + amount = Amount( + rawValue: txn.outputs + .where( + (e) => + e.walletOwns && + !e.addresses.contains(sparkChangeAddress!), + ) + .fold(BigInt.zero, (p, e) => p + e.value), + fractionDigits: amountFormatter.coin.fractionDigits, + ); + } else { + amount = txn.getAmountReceivedInThisWallet( + fractionDigits: fractionDigits, + ); + } + break; + + case TransactionType.unknown: + amount = txn.getAmountSentFromThisWallet( + fractionDigits: fractionDigits, + ); + break; + } + } + + return amountFormatter.format( + amount, + ethContract: ethContract, + withUnitName: false, + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index f311f27ea..cfc287042 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -376,6 +376,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" + csv: + dependency: "direct main" + description: + name: csv + sha256: c6aa2679b2a18cb57652920f674488d89712efaf4d3fdf2e537215b35fc19d6c + url: "https://pub.dev" + source: hosted + version: "6.0.0" cw_core: dependency: "direct main" description: diff --git a/scripts/app_config/templates/pubspec.template b/scripts/app_config/templates/pubspec.template index a784dbabd..5474cfcf7 100644 --- a/scripts/app_config/templates/pubspec.template +++ b/scripts/app_config/templates/pubspec.template @@ -190,6 +190,7 @@ dependencies: calendar_date_picker2: ^1.0.2 sqlite3: ^2.4.3 sqlite3_flutter_libs: ^0.5.22 + csv: ^6.0.0 dev_dependencies: flutter_test: