mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2024-11-19 18:11:36 +01:00
141 lines
5.2 KiB
C#
141 lines
5.2 KiB
C#
#nullable enable
|
|
using System;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using BTCPayServer.Data;
|
|
using BTCPayServer.Services.Stores;
|
|
using BTCPayServer.Services.Wallets;
|
|
using Dapper;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using NBitcoin;
|
|
using NBXplorer.DerivationStrategy;
|
|
using static Microsoft.EntityFrameworkCore.DbLoggerCategory.Database;
|
|
|
|
namespace BTCPayServer.Services.Reporting;
|
|
|
|
public class OnChainWalletReportProvider : ReportProvider
|
|
{
|
|
public OnChainWalletReportProvider(
|
|
NBXplorerConnectionFactory NbxplorerConnectionFactory,
|
|
StoreRepository storeRepository,
|
|
DisplayFormatter displayFormatter,
|
|
BTCPayNetworkProvider networkProvider,
|
|
WalletRepository walletRepository)
|
|
{
|
|
this.NbxplorerConnectionFactory = NbxplorerConnectionFactory;
|
|
StoreRepository = storeRepository;
|
|
NetworkProvider = networkProvider;
|
|
WalletRepository = walletRepository;
|
|
_displayFormatter = displayFormatter;
|
|
}
|
|
|
|
private readonly DisplayFormatter _displayFormatter;
|
|
private NBXplorerConnectionFactory NbxplorerConnectionFactory { get; }
|
|
private StoreRepository StoreRepository { get; }
|
|
private BTCPayNetworkProvider NetworkProvider { get; }
|
|
private WalletRepository WalletRepository { get; }
|
|
public override string Name => "On-Chain Wallets";
|
|
ViewDefinition CreateViewDefinition()
|
|
{
|
|
return new()
|
|
{
|
|
Fields =
|
|
{
|
|
new ("Date", "datetime"),
|
|
new ("Crypto", "string"),
|
|
// For proper rendering of explorer links, Crypto should always be before tx_id
|
|
new ("TransactionId", "tx_id"),
|
|
new ("InvoiceId", "invoice_id"),
|
|
new ("Confirmed", "boolean"),
|
|
new ("BalanceChange", "amount")
|
|
},
|
|
Charts =
|
|
{
|
|
new ()
|
|
{
|
|
Name = "Group by Crypto",
|
|
Totals = { "Crypto" },
|
|
Groups = { "Crypto", "Confirmed" },
|
|
Aggregates = { "BalanceChange" }
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
public override bool IsAvailable()
|
|
{
|
|
return NbxplorerConnectionFactory.Available;
|
|
}
|
|
|
|
public override async Task Query(QueryContext queryContext, CancellationToken cancellation)
|
|
{
|
|
queryContext.ViewDefinition = CreateViewDefinition();
|
|
await using var conn = await NbxplorerConnectionFactory.OpenConnection();
|
|
var store = await StoreRepository.FindStore(queryContext.StoreId);
|
|
if (store is null)
|
|
return;
|
|
var interval = DateTimeOffset.UtcNow - queryContext.From;
|
|
foreach (var settings in store.GetDerivationSchemeSettings(NetworkProvider))
|
|
{
|
|
var walletId = new WalletId(store.Id, settings.Network.CryptoCode);
|
|
var command = new CommandDefinition(
|
|
commandText:
|
|
"SELECT r.tx_id, r.seen_at, t.blk_id, t.blk_height, r.balance_change " +
|
|
"FROM get_wallets_recent(@wallet_id, @code, @asset_id, @interval, NULL, NULL) r " +
|
|
"JOIN txs t USING (code, tx_id) " +
|
|
"ORDER BY r.seen_at",
|
|
parameters: new
|
|
{
|
|
asset_id = GetAssetId(settings.Network),
|
|
wallet_id = NBXplorer.Client.DBUtils.nbxv1_get_wallet_id(settings.Network.CryptoCode, settings.AccountDerivation.ToString()),
|
|
code = settings.Network.CryptoCode,
|
|
interval
|
|
},
|
|
cancellationToken: cancellation);
|
|
|
|
var rows = await conn.QueryAsync(command);
|
|
foreach (var r in rows)
|
|
{
|
|
var date = (DateTimeOffset)r.seen_at;
|
|
if (date > queryContext.To)
|
|
continue;
|
|
var values = queryContext.AddData();
|
|
var balanceChange = Money.Satoshis((long)r.balance_change).ToDecimal(MoneyUnit.BTC);
|
|
values.Add(date);
|
|
values.Add(settings.Network.CryptoCode);
|
|
values.Add((string)r.tx_id);
|
|
values.Add(null);
|
|
values.Add((long?)r.blk_height is not null);
|
|
values.Add(new FormattedAmount(balanceChange, settings.Network.Divisibility).ToJObject());
|
|
}
|
|
var objects = await WalletRepository.GetWalletObjects(new GetWalletObjectsQuery
|
|
{
|
|
Ids = queryContext.Data.Select(d => (string)d[2]!).ToArray(),
|
|
WalletId = walletId,
|
|
Type = "tx"
|
|
});
|
|
foreach (var row in queryContext.Data)
|
|
{
|
|
if (!objects.TryGetValue(new WalletObjectId(walletId, "tx", (string)row[2]!), out var txObject))
|
|
continue;
|
|
var invoiceId = txObject.GetLinks().Where(t => t.type == "invoice").Select(t => t.id).FirstOrDefault();
|
|
row[3] = invoiceId;
|
|
}
|
|
}
|
|
}
|
|
|
|
private string? GetAssetId(BTCPayNetwork network)
|
|
{
|
|
#if ALTCOINS
|
|
if (network is ElementsBTCPayNetwork elNetwork)
|
|
{
|
|
if (elNetwork.CryptoCode == elNetwork.NetworkCryptoCode)
|
|
return "";
|
|
return elNetwork.AssetId.ToString();
|
|
}
|
|
#endif
|
|
return null;
|
|
}
|
|
}
|