2023-03-27 06:59:33 +02:00
|
|
|
#nullable enable
|
2022-05-20 02:35:31 +02:00
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Globalization;
|
|
|
|
using System.IO;
|
|
|
|
using System.Linq;
|
2023-03-27 06:59:33 +02:00
|
|
|
using System.Text;
|
2022-05-20 02:35:31 +02:00
|
|
|
using BTCPayServer.Data;
|
|
|
|
using CsvHelper.Configuration;
|
|
|
|
using CsvHelper.Configuration.Attributes;
|
|
|
|
using Newtonsoft.Json;
|
|
|
|
|
|
|
|
namespace BTCPayServer.Services.Wallets.Export
|
|
|
|
{
|
|
|
|
public class TransactionsExport
|
|
|
|
{
|
|
|
|
private readonly BTCPayWallet _wallet;
|
|
|
|
private readonly Dictionary<string, WalletTransactionInfo> _walletTransactionsInfo;
|
|
|
|
|
|
|
|
public TransactionsExport(BTCPayWallet wallet, Dictionary<string, WalletTransactionInfo> walletTransactionsInfo)
|
|
|
|
{
|
|
|
|
_wallet = wallet;
|
|
|
|
_walletTransactionsInfo = walletTransactionsInfo;
|
|
|
|
}
|
|
|
|
|
2022-07-04 15:50:56 +09:00
|
|
|
public string Process(IEnumerable<TransactionHistoryLine> inputList, string fileFormat)
|
2022-05-20 02:35:31 +02:00
|
|
|
{
|
|
|
|
var list = inputList.Select(tx =>
|
|
|
|
{
|
|
|
|
var model = new ExportTransaction
|
|
|
|
{
|
|
|
|
TransactionId = tx.TransactionId.ToString(),
|
2022-07-04 15:50:56 +09:00
|
|
|
Timestamp = tx.SeenAt,
|
2022-05-20 02:35:31 +02:00
|
|
|
Amount = tx.BalanceChange.ShowMoney(_wallet.Network),
|
|
|
|
Currency = _wallet.Network.CryptoCode,
|
|
|
|
IsConfirmed = tx.Confirmations != 0
|
|
|
|
};
|
2023-01-06 14:18:07 +01:00
|
|
|
|
2022-05-20 02:35:31 +02:00
|
|
|
if (_walletTransactionsInfo.TryGetValue(tx.TransactionId.ToString(), out var transactionInfo))
|
|
|
|
{
|
2023-03-27 06:59:33 +02:00
|
|
|
model.Labels = transactionInfo.LabelColors.Select(l => l.Key).ToList();
|
2022-05-20 02:35:31 +02:00
|
|
|
model.Comment = transactionInfo.Comment;
|
|
|
|
}
|
|
|
|
|
|
|
|
return model;
|
|
|
|
}).ToList();
|
2023-04-10 11:07:03 +09:00
|
|
|
|
2023-03-27 06:59:33 +02:00
|
|
|
return fileFormat switch
|
|
|
|
{
|
|
|
|
"bip329" => ProcessBip329(list),
|
|
|
|
"json" => ProcessJson(list),
|
|
|
|
"csv" => ProcessCsv(list),
|
|
|
|
_ => throw new Exception("Export format not supported")
|
|
|
|
};
|
|
|
|
}
|
2022-05-20 02:35:31 +02:00
|
|
|
|
2023-03-27 06:59:33 +02:00
|
|
|
// https://github.com/bitcoin/bips/blob/master/bip-0329.mediawiki
|
|
|
|
private static string ProcessBip329(List<ExportTransaction> txs)
|
|
|
|
{
|
|
|
|
var sw = new StringWriter();
|
|
|
|
var jsonw = new JsonTextWriter(sw);
|
|
|
|
foreach (var tx in txs)
|
|
|
|
{
|
|
|
|
if (tx.Labels is null)
|
|
|
|
continue;
|
|
|
|
foreach (var label in tx.Labels)
|
|
|
|
{
|
|
|
|
jsonw.WriteStartObject();
|
|
|
|
jsonw.WritePropertyName("type");
|
|
|
|
jsonw.WriteValue("tx");
|
|
|
|
jsonw.WritePropertyName("ref");
|
|
|
|
jsonw.WriteValue(tx.TransactionId);
|
|
|
|
jsonw.WritePropertyName("label");
|
|
|
|
jsonw.WriteValue(label);
|
|
|
|
jsonw.WriteEndObject();
|
|
|
|
jsonw.WriteWhitespace("\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
jsonw.Flush();
|
|
|
|
return sw.ToString();
|
2022-05-20 02:35:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private static string ProcessJson(List<ExportTransaction> invoices)
|
|
|
|
{
|
|
|
|
var serializerSett = new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore };
|
|
|
|
var json = JsonConvert.SerializeObject(invoices, Formatting.Indented, serializerSett);
|
|
|
|
return json;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static string ProcessCsv(IEnumerable<ExportTransaction> invoices)
|
|
|
|
{
|
|
|
|
using StringWriter writer = new();
|
|
|
|
using var csvWriter = new CsvHelper.CsvWriter(writer, new CsvConfiguration(CultureInfo.InvariantCulture), true);
|
|
|
|
csvWriter.Configuration.RegisterClassMap<ExportTransactionMap>();
|
|
|
|
csvWriter.WriteHeader<ExportTransaction>();
|
|
|
|
csvWriter.NextRecord();
|
|
|
|
csvWriter.WriteRecords(invoices);
|
|
|
|
csvWriter.Flush();
|
|
|
|
return writer.ToString();
|
|
|
|
}
|
|
|
|
}
|
2023-01-06 14:18:07 +01:00
|
|
|
|
2022-05-20 02:35:31 +02:00
|
|
|
public sealed class ExportTransactionMap : ClassMap<ExportTransaction>
|
|
|
|
{
|
|
|
|
public ExportTransactionMap()
|
|
|
|
{
|
|
|
|
AutoMap(CultureInfo.InvariantCulture);
|
|
|
|
Map(m => m.Labels).ConvertUsing(row => row.Labels == null ? string.Empty : string.Join(", ", row.Labels));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public class ExportTransaction
|
|
|
|
{
|
|
|
|
[Name("Transaction Id")]
|
2023-03-27 06:59:33 +02:00
|
|
|
public string TransactionId { get; set; } = string.Empty;
|
2022-05-20 02:35:31 +02:00
|
|
|
public DateTimeOffset Timestamp { get; set; }
|
2023-03-27 06:59:33 +02:00
|
|
|
public string Amount { get; set; } = string.Empty;
|
|
|
|
public string Currency { get; set; } = string.Empty;
|
2023-01-06 14:18:07 +01:00
|
|
|
|
2022-05-20 02:35:31 +02:00
|
|
|
[Name("Is Confirmed")]
|
|
|
|
public bool IsConfirmed { get; set; }
|
2023-03-27 06:59:33 +02:00
|
|
|
public string? Comment { get; set; }
|
|
|
|
public List<string>? Labels { get; set; }
|
2022-05-20 02:35:31 +02:00
|
|
|
}
|
|
|
|
}
|