using System; using System.Collections.Generic; using System.Globalization; using System.Threading.Tasks; using BTCPayServer.Client.JsonConverters; using BTCPayServer.Data; using Dapper; namespace BTCPayServer.Services.Wallets; public enum WalletHistogramType { Week, Month, Year } public class WalletHistogramService { private readonly BTCPayNetworkProvider _networkProvider; private readonly NBXplorerConnectionFactory _connectionFactory; public WalletHistogramService( BTCPayNetworkProvider networkProvider, NBXplorerConnectionFactory connectionFactory) { _networkProvider = networkProvider; _connectionFactory = connectionFactory; } public async Task GetHistogram(StoreData store, WalletId walletId, WalletHistogramType type) { // https://github.com/dgarage/NBXplorer/blob/master/docs/Postgres-Schema.md if (_connectionFactory.Available) { var derivationSettings = store.GetDerivationSchemeSettings(_networkProvider, walletId.CryptoCode); if (derivationSettings != null) { var wallet_id = derivationSettings.GetNBXWalletId(); await using var conn = await _connectionFactory.OpenConnection(); var code = walletId.CryptoCode; var to = DateTimeOffset.UtcNow; var labelCount = 6; (var days, var pointCount) = type switch { WalletHistogramType.Week => (7, 30), WalletHistogramType.Month => (30, 30), WalletHistogramType.Year => (365, 30), _ => throw new ArgumentException($"WalletHistogramType {type} does not exist.") }; var from = to - TimeSpan.FromDays(days); var interval = TimeSpan.FromTicks((to - from).Ticks / pointCount); var balance = await conn.ExecuteScalarAsync( "SELECT to_btc(available_balance) FROM wallets_balances WHERE wallet_id=@wallet_id AND code=@code AND asset_id=''", new { code, wallet_id }); var rows = await conn.QueryAsync("SELECT date, to_btc(balance) balance FROM get_wallets_histogram(@wallet_id, @code, '', @from, @to, @interval)", new { code, wallet_id, from, to, interval }); var data = rows.AsList(); var series = new List(pointCount); var labels = new List(labelCount); var labelEvery = pointCount / labelCount; for (int i = 0; i < data.Count; i++) { var r = data[i]; series.Add((decimal)r.balance); labels.Add((i % labelEvery == 0) ? ((DateTime)r.date).ToString("MMM dd", CultureInfo.InvariantCulture) : null); } series[^1] = balance; return new WalletHistogramData { Series = series, Labels = labels, Balance = balance, Type = type }; } } return null; } } public class WalletHistogramData { public WalletHistogramType Type { get; set; } public List Series { get; set; } public List Labels { get; set; } public decimal Balance { get; set; } }