mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-18 14:34:23 +01:00
BTCWallet is single currency, introduce BTCWalletProvider
This commit is contained in:
@@ -66,13 +66,17 @@ namespace BTCPayServer.Controllers
|
|||||||
public async Task<IActionResult> PostPayment(string invoiceId, string cryptoCode = null)
|
public async Task<IActionResult> PostPayment(string invoiceId, string cryptoCode = null)
|
||||||
{
|
{
|
||||||
var invoice = await _InvoiceRepository.GetInvoice(null, invoiceId);
|
var invoice = await _InvoiceRepository.GetInvoice(null, invoiceId);
|
||||||
if (invoice == null || invoice.IsExpired())
|
|
||||||
return NotFound();
|
|
||||||
if (cryptoCode == null)
|
if (cryptoCode == null)
|
||||||
cryptoCode = "BTC";
|
cryptoCode = "BTC";
|
||||||
var network = _NetworkProvider.GetNetwork(cryptoCode);
|
var network = _NetworkProvider.GetNetwork(cryptoCode);
|
||||||
|
if (network == null || invoice == null || invoice.IsExpired() || !invoice.Support(network))
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
var wallet = _WalletProvider.GetWallet(network);
|
||||||
|
if (wallet == null)
|
||||||
|
return NotFound();
|
||||||
var payment = PaymentMessage.Load(Request.Body);
|
var payment = PaymentMessage.Load(Request.Body);
|
||||||
var unused = _Wallet.BroadcastTransactionsAsync(network, payment.Transactions);
|
var unused = wallet.BroadcastTransactionsAsync(payment.Transactions);
|
||||||
await _InvoiceRepository.AddRefundsAsync(invoiceId, payment.RefundTo.Select(p => new TxOut(p.Amount, p.Script)).ToArray());
|
await _InvoiceRepository.AddRefundsAsync(invoiceId, payment.RefundTo.Select(p => new TxOut(p.Amount, p.Script)).ToArray());
|
||||||
return new PaymentAckActionResult(payment.CreateACK(invoiceId + " is currently processing, thanks for your purchase..."));
|
return new PaymentAckActionResult(payment.CreateACK(invoiceId + " is currently processing, thanks for your purchase..."));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ namespace BTCPayServer.Controllers
|
|||||||
}).ToList()
|
}).ToList()
|
||||||
};
|
};
|
||||||
|
|
||||||
var isMultiCurrency = invoice.Payments.Select(p=>p.GetCryptoCode()).Concat(new[] { network.CryptoCode }).Distinct().Count() > 1;
|
var isMultiCurrency = invoice.GetPayments().Select(p=>p.GetCryptoCode()).Concat(new[] { network.CryptoCode }).Distinct().Count() > 1;
|
||||||
if (isMultiCurrency)
|
if (isMultiCurrency)
|
||||||
model.NetworkFeeDescription = $"{accounting.NetworkFee} {network.CryptoCode}";
|
model.NetworkFeeDescription = $"{accounting.NetworkFee} {network.CryptoCode}";
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ namespace BTCPayServer.Controllers
|
|||||||
public partial class InvoiceController : Controller
|
public partial class InvoiceController : Controller
|
||||||
{
|
{
|
||||||
InvoiceRepository _InvoiceRepository;
|
InvoiceRepository _InvoiceRepository;
|
||||||
BTCPayWallet _Wallet;
|
BTCPayWalletProvider _WalletProvider;
|
||||||
IRateProviderFactory _RateProviders;
|
IRateProviderFactory _RateProviders;
|
||||||
StoreRepository _StoreRepository;
|
StoreRepository _StoreRepository;
|
||||||
UserManager<ApplicationUser> _UserManager;
|
UserManager<ApplicationUser> _UserManager;
|
||||||
@@ -57,7 +57,7 @@ namespace BTCPayServer.Controllers
|
|||||||
public InvoiceController(InvoiceRepository invoiceRepository,
|
public InvoiceController(InvoiceRepository invoiceRepository,
|
||||||
CurrencyNameTable currencyNameTable,
|
CurrencyNameTable currencyNameTable,
|
||||||
UserManager<ApplicationUser> userManager,
|
UserManager<ApplicationUser> userManager,
|
||||||
BTCPayWallet wallet,
|
BTCPayWalletProvider walletProvider,
|
||||||
IRateProviderFactory rateProviders,
|
IRateProviderFactory rateProviders,
|
||||||
StoreRepository storeRepository,
|
StoreRepository storeRepository,
|
||||||
EventAggregator eventAggregator,
|
EventAggregator eventAggregator,
|
||||||
@@ -69,7 +69,7 @@ namespace BTCPayServer.Controllers
|
|||||||
_CurrencyNameTable = currencyNameTable ?? throw new ArgumentNullException(nameof(currencyNameTable));
|
_CurrencyNameTable = currencyNameTable ?? throw new ArgumentNullException(nameof(currencyNameTable));
|
||||||
_StoreRepository = storeRepository ?? throw new ArgumentNullException(nameof(storeRepository));
|
_StoreRepository = storeRepository ?? throw new ArgumentNullException(nameof(storeRepository));
|
||||||
_InvoiceRepository = invoiceRepository ?? throw new ArgumentNullException(nameof(invoiceRepository));
|
_InvoiceRepository = invoiceRepository ?? throw new ArgumentNullException(nameof(invoiceRepository));
|
||||||
_Wallet = wallet ?? throw new ArgumentNullException(nameof(wallet));
|
_WalletProvider = walletProvider ?? throw new ArgumentNullException(nameof(walletProvider));
|
||||||
_RateProviders = rateProviders ?? throw new ArgumentNullException(nameof(rateProviders));
|
_RateProviders = rateProviders ?? throw new ArgumentNullException(nameof(rateProviders));
|
||||||
_UserManager = userManager;
|
_UserManager = userManager;
|
||||||
_FeeProviderFactory = feeProviderFactory ?? throw new ArgumentNullException(nameof(feeProviderFactory));
|
_FeeProviderFactory = feeProviderFactory ?? throw new ArgumentNullException(nameof(feeProviderFactory));
|
||||||
@@ -116,14 +116,20 @@ namespace BTCPayServer.Controllers
|
|||||||
entity.SpeedPolicy = ParseSpeedPolicy(invoice.TransactionSpeed, store.SpeedPolicy);
|
entity.SpeedPolicy = ParseSpeedPolicy(invoice.TransactionSpeed, store.SpeedPolicy);
|
||||||
|
|
||||||
var queries = derivationStrategies
|
var queries = derivationStrategies
|
||||||
.Select(derivationStrategy =>
|
.Select(derivationStrategy => ( Wallet: _WalletProvider.GetWallet(derivationStrategy.Network),
|
||||||
|
DerivationStrategy: derivationStrategy.DerivationStrategyBase,
|
||||||
|
Network: derivationStrategy.Network,
|
||||||
|
RateProvider: _RateProviders.GetRateProvider(derivationStrategy.Network),
|
||||||
|
FeeRateProvider: _FeeProviderFactory.CreateFeeProvider(derivationStrategy.Network)))
|
||||||
|
.Where(_ => _.Wallet != null && _.FeeRateProvider != null && _.RateProvider != null)
|
||||||
|
.Select(_ =>
|
||||||
{
|
{
|
||||||
return new
|
return new
|
||||||
{
|
{
|
||||||
network = derivationStrategy.Network,
|
network = _.Network,
|
||||||
getFeeRate = _FeeProviderFactory.CreateFeeProvider(derivationStrategy.Network).GetFeeRateAsync(),
|
getFeeRate = _.FeeRateProvider.GetFeeRateAsync(),
|
||||||
getRate = _RateProviders.GetRateProvider(derivationStrategy.Network).GetRateAsync(invoice.Currency),
|
getRate = _.RateProvider.GetRateAsync(invoice.Currency),
|
||||||
getAddress = _Wallet.ReserveAddressAsync(derivationStrategy)
|
getAddress = _.Wallet.ReserveAddressAsync(_.DerivationStrategy)
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ namespace BTCPayServer.Controllers
|
|||||||
TokenRepository tokenRepo,
|
TokenRepository tokenRepo,
|
||||||
UserManager<ApplicationUser> userManager,
|
UserManager<ApplicationUser> userManager,
|
||||||
AccessTokenController tokenController,
|
AccessTokenController tokenController,
|
||||||
BTCPayWallet wallet,
|
BTCPayWalletProvider walletProvider,
|
||||||
BTCPayNetworkProvider networkProvider,
|
BTCPayNetworkProvider networkProvider,
|
||||||
ExplorerClientProvider explorerProvider,
|
ExplorerClientProvider explorerProvider,
|
||||||
IHostingEnvironment env)
|
IHostingEnvironment env)
|
||||||
@@ -41,14 +41,14 @@ namespace BTCPayServer.Controllers
|
|||||||
_TokenRepository = tokenRepo;
|
_TokenRepository = tokenRepo;
|
||||||
_UserManager = userManager;
|
_UserManager = userManager;
|
||||||
_TokenController = tokenController;
|
_TokenController = tokenController;
|
||||||
_Wallet = wallet;
|
_WalletProvider = walletProvider;
|
||||||
_Env = env;
|
_Env = env;
|
||||||
_NetworkProvider = networkProvider;
|
_NetworkProvider = networkProvider;
|
||||||
_ExplorerProvider = explorerProvider;
|
_ExplorerProvider = explorerProvider;
|
||||||
}
|
}
|
||||||
BTCPayNetworkProvider _NetworkProvider;
|
BTCPayNetworkProvider _NetworkProvider;
|
||||||
private ExplorerClientProvider _ExplorerProvider;
|
private ExplorerClientProvider _ExplorerProvider;
|
||||||
BTCPayWallet _Wallet;
|
BTCPayWalletProvider _WalletProvider;
|
||||||
AccessTokenController _TokenController;
|
AccessTokenController _TokenController;
|
||||||
StoreRepository _Repo;
|
StoreRepository _Repo;
|
||||||
TokenRepository _TokenRepository;
|
TokenRepository _TokenRepository;
|
||||||
@@ -95,7 +95,10 @@ namespace BTCPayServer.Controllers
|
|||||||
var stores = await _Repo.GetStoresByUserId(GetUserId());
|
var stores = await _Repo.GetStoresByUserId(GetUserId());
|
||||||
var balances = stores
|
var balances = stores
|
||||||
.Select(s => s.GetDerivationStrategies(_NetworkProvider)
|
.Select(s => s.GetDerivationStrategies(_NetworkProvider)
|
||||||
.Select(async ss => (await _Wallet.GetBalance(ss)).ToString() + " " + ss.Network.CryptoCode))
|
.Select(d => (Wallet: _WalletProvider.GetWallet(d.Network),
|
||||||
|
DerivationStrategy: d.DerivationStrategyBase))
|
||||||
|
.Where(_ => _.Wallet != null)
|
||||||
|
.Select(async _ => (await _.Wallet.GetBalance(_.DerivationStrategy)).ToString() + " " + _.Wallet.Network.CryptoCode))
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
await Task.WhenAll(balances.SelectMany(_ => _));
|
await Task.WhenAll(balances.SelectMany(_ => _));
|
||||||
@@ -210,6 +213,12 @@ namespace BTCPayServer.Controllers
|
|||||||
ModelState.AddModelError(nameof(vm.CryptoCurrency), "Invalid network");
|
ModelState.AddModelError(nameof(vm.CryptoCurrency), "Invalid network");
|
||||||
return View(vm);
|
return View(vm);
|
||||||
}
|
}
|
||||||
|
var wallet = _WalletProvider.GetWallet(network);
|
||||||
|
if (wallet == null)
|
||||||
|
{
|
||||||
|
ModelState.AddModelError(nameof(vm.CryptoCurrency), "Invalid network");
|
||||||
|
return View(vm);
|
||||||
|
}
|
||||||
|
|
||||||
if (command == "Save")
|
if (command == "Save")
|
||||||
{
|
{
|
||||||
@@ -218,7 +227,7 @@ namespace BTCPayServer.Controllers
|
|||||||
if (!string.IsNullOrEmpty(vm.DerivationScheme))
|
if (!string.IsNullOrEmpty(vm.DerivationScheme))
|
||||||
{
|
{
|
||||||
var strategy = ParseDerivationStrategy(vm.DerivationScheme, vm.DerivationSchemeFormat, network);
|
var strategy = ParseDerivationStrategy(vm.DerivationScheme, vm.DerivationSchemeFormat, network);
|
||||||
await _Wallet.TrackAsync(strategy);
|
await wallet.TrackAsync(strategy);
|
||||||
vm.DerivationScheme = strategy.ToString();
|
vm.DerivationScheme = strategy.ToString();
|
||||||
}
|
}
|
||||||
store.SetDerivationStrategy(network, vm.DerivationScheme);
|
store.SetDerivationStrategy(network, vm.DerivationScheme);
|
||||||
@@ -240,12 +249,12 @@ namespace BTCPayServer.Controllers
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var scheme = ParseDerivationStrategy(vm.DerivationScheme, vm.DerivationSchemeFormat, network);
|
var scheme = ParseDerivationStrategy(vm.DerivationScheme, vm.DerivationSchemeFormat, network);
|
||||||
var line = scheme.DerivationStrategyBase.GetLineFor(DerivationFeature.Deposit);
|
var line = scheme.GetLineFor(DerivationFeature.Deposit);
|
||||||
|
|
||||||
for (int i = 0; i < 10; i++)
|
for (int i = 0; i < 10; i++)
|
||||||
{
|
{
|
||||||
var address = line.Derive((uint)i);
|
var address = line.Derive((uint)i);
|
||||||
vm.AddressSamples.Add((line.Path.Derive((uint)i).ToString(), address.ScriptPubKey.GetDestinationAddress(scheme.Network.NBitcoinNetwork).ToString()));
|
vm.AddressSamples.Add((line.Path.Derive((uint)i).ToString(), address.ScriptPubKey.GetDestinationAddress(network.NBitcoinNetwork).ToString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
@@ -315,7 +324,7 @@ namespace BTCPayServer.Controllers
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private DerivationStrategy ParseDerivationStrategy(string derivationScheme, string format, BTCPayNetwork network)
|
private DerivationStrategyBase ParseDerivationStrategy(string derivationScheme, string format, BTCPayNetwork network)
|
||||||
{
|
{
|
||||||
if (format == "Electrum")
|
if (format == "Electrum")
|
||||||
{
|
{
|
||||||
@@ -349,7 +358,7 @@ namespace BTCPayServer.Controllers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return DerivationStrategy.Parse(new DerivationStrategyFactory(network.NBitcoinNetwork).Parse(derivationScheme).ToString(), network);
|
return new DerivationStrategyFactory(network.NBitcoinNetwork).Parse(derivationScheme);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ namespace BTCPayServer.HostedServices
|
|||||||
|
|
||||||
InvoiceRepository _InvoiceRepository;
|
InvoiceRepository _InvoiceRepository;
|
||||||
EventAggregator _EventAggregator;
|
EventAggregator _EventAggregator;
|
||||||
BTCPayWallet _Wallet;
|
BTCPayWalletProvider _WalletProvider;
|
||||||
BTCPayNetworkProvider _NetworkProvider;
|
BTCPayNetworkProvider _NetworkProvider;
|
||||||
|
|
||||||
public InvoiceWatcher(
|
public InvoiceWatcher(
|
||||||
@@ -53,10 +53,10 @@ namespace BTCPayServer.HostedServices
|
|||||||
BTCPayNetworkProvider networkProvider,
|
BTCPayNetworkProvider networkProvider,
|
||||||
InvoiceRepository invoiceRepository,
|
InvoiceRepository invoiceRepository,
|
||||||
EventAggregator eventAggregator,
|
EventAggregator eventAggregator,
|
||||||
BTCPayWallet wallet)
|
BTCPayWalletProvider walletProvider)
|
||||||
{
|
{
|
||||||
PollInterval = TimeSpan.FromMinutes(1.0);
|
PollInterval = TimeSpan.FromMinutes(1.0);
|
||||||
_Wallet = wallet ?? throw new ArgumentNullException(nameof(wallet));
|
_WalletProvider = walletProvider ?? throw new ArgumentNullException(nameof(walletProvider));
|
||||||
_InvoiceRepository = invoiceRepository ?? throw new ArgumentNullException(nameof(invoiceRepository));
|
_InvoiceRepository = invoiceRepository ?? throw new ArgumentNullException(nameof(invoiceRepository));
|
||||||
_EventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
|
_EventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
|
||||||
_NetworkProvider = networkProvider;
|
_NetworkProvider = networkProvider;
|
||||||
@@ -150,29 +150,39 @@ namespace BTCPayServer.HostedServices
|
|||||||
}
|
}
|
||||||
|
|
||||||
var derivationStrategies = invoice.GetDerivationStrategies(_NetworkProvider).ToArray();
|
var derivationStrategies = invoice.GetDerivationStrategies(_NetworkProvider).ToArray();
|
||||||
foreach (NetworkCoins coins in await GetCoinsPerNetwork(context, invoice, derivationStrategies))
|
var payments = await GetPaymentsWithTransaction(derivationStrategies, invoice);
|
||||||
|
foreach (Task<NetworkCoins> coinsAsync in GetCoinsPerNetwork(context, invoice, derivationStrategies))
|
||||||
{
|
{
|
||||||
|
var coins = await coinsAsync;
|
||||||
|
if (coins.TimestampedCoins.Length == 0)
|
||||||
|
continue;
|
||||||
bool dirtyAddress = false;
|
bool dirtyAddress = false;
|
||||||
if (coins.State != null)
|
if (coins.State != null)
|
||||||
context.ModifiedKnownStates.AddOrReplace(coins.Strategy.Network, coins.State);
|
context.ModifiedKnownStates.AddOrReplace(coins.Wallet.Network, coins.State);
|
||||||
var alreadyAccounted = new HashSet<OutPoint>(invoice.GetPayments(coins.Strategy.Network).Select(p => p.Outpoint));
|
var alreadyAccounted = new HashSet<OutPoint>(invoice.GetPayments(coins.Wallet.Network).Select(p => p.Outpoint));
|
||||||
|
|
||||||
foreach (var coin in coins.TimestampedCoins.Where(c => !alreadyAccounted.Contains(c.Coin.Outpoint)))
|
foreach (var coin in coins.TimestampedCoins.Where(c => !alreadyAccounted.Contains(c.Coin.Outpoint)))
|
||||||
{
|
{
|
||||||
var payment = await _InvoiceRepository.AddPayment(invoice.Id, coin.DateTime, coin.Coin, coins.Strategy.Network.CryptoCode).ConfigureAwait(false);
|
var payment = await _InvoiceRepository.AddPayment(invoice.Id, coin.DateTime, coin.Coin, coins.Wallet.Network.CryptoCode).ConfigureAwait(false);
|
||||||
#pragma warning disable CS0618
|
#pragma warning disable CS0618
|
||||||
invoice.Payments.Add(payment);
|
invoice.Payments.Add(payment);
|
||||||
#pragma warning restore CS0618
|
#pragma warning restore CS0618
|
||||||
|
alreadyAccounted.Add(coin.Coin.Outpoint);
|
||||||
context.Events.Add(new InvoicePaymentEvent(invoice.Id));
|
context.Events.Add(new InvoicePaymentEvent(invoice.Id));
|
||||||
dirtyAddress = true;
|
dirtyAddress = true;
|
||||||
}
|
}
|
||||||
var network = coins.Strategy.Network;
|
if (dirtyAddress)
|
||||||
|
{
|
||||||
|
payments = await GetPaymentsWithTransaction(derivationStrategies, invoice);
|
||||||
|
}
|
||||||
|
var network = coins.Wallet.Network;
|
||||||
var cryptoData = invoice.GetCryptoData(network);
|
var cryptoData = invoice.GetCryptoData(network);
|
||||||
var cryptoDataAll = invoice.GetCryptoData();
|
var cryptoDataAll = invoice.GetCryptoData();
|
||||||
var accounting = cryptoData.Calculate();
|
var accounting = cryptoData.Calculate();
|
||||||
|
|
||||||
if (invoice.Status == "new" || invoice.Status == "expired")
|
if (invoice.Status == "new" || invoice.Status == "expired")
|
||||||
{
|
{
|
||||||
var totalPaid = (await GetPaymentsWithTransaction(derivationStrategies, invoice)).Select(p => p.Payment.GetValue(cryptoDataAll, cryptoData.CryptoCode)).Sum();
|
var totalPaid = payments.Select(p => p.Payment.GetValue(cryptoDataAll, cryptoData.CryptoCode)).Sum();
|
||||||
if (totalPaid >= accounting.TotalDue)
|
if (totalPaid >= accounting.TotalDue)
|
||||||
{
|
{
|
||||||
if (invoice.Status == "new")
|
if (invoice.Status == "new")
|
||||||
@@ -203,7 +213,7 @@ namespace BTCPayServer.HostedServices
|
|||||||
context.MarkDirty();
|
context.MarkDirty();
|
||||||
if (dirtyAddress)
|
if (dirtyAddress)
|
||||||
{
|
{
|
||||||
var address = await _Wallet.ReserveAddressAsync(coins.Strategy);
|
var address = await coins.Wallet.ReserveAddressAsync(coins.Strategy);
|
||||||
Logs.PayServer.LogInformation("Generate new " + address);
|
Logs.PayServer.LogInformation("Generate new " + address);
|
||||||
await _InvoiceRepository.NewAddress(invoice.Id, address, network);
|
await _InvoiceRepository.NewAddress(invoice.Id, address, network);
|
||||||
}
|
}
|
||||||
@@ -212,7 +222,7 @@ namespace BTCPayServer.HostedServices
|
|||||||
|
|
||||||
if (invoice.Status == "paid")
|
if (invoice.Status == "paid")
|
||||||
{
|
{
|
||||||
var transactions = await GetPaymentsWithTransaction(derivationStrategies, invoice);
|
var transactions = payments;
|
||||||
if (invoice.SpeedPolicy == SpeedPolicy.HighSpeed)
|
if (invoice.SpeedPolicy == SpeedPolicy.HighSpeed)
|
||||||
{
|
{
|
||||||
transactions = transactions.Where(t => t.Confirmations >= 1 || !t.Transaction.RBF);
|
transactions = transactions.Where(t => t.Confirmations >= 1 || !t.Transaction.RBF);
|
||||||
@@ -250,7 +260,7 @@ namespace BTCPayServer.HostedServices
|
|||||||
|
|
||||||
if (invoice.Status == "confirmed")
|
if (invoice.Status == "confirmed")
|
||||||
{
|
{
|
||||||
var transactions = await GetPaymentsWithTransaction(derivationStrategies, invoice);
|
var transactions = payments;
|
||||||
transactions = transactions.Where(t => t.Confirmations >= 6);
|
transactions = transactions.Where(t => t.Confirmations >= 6);
|
||||||
var totalConfirmed = transactions.Select(t => t.Payment.GetValue(cryptoDataAll, cryptoData.CryptoCode)).Sum();
|
var totalConfirmed = transactions.Select(t => t.Payment.GetValue(cryptoDataAll, cryptoData.CryptoCode)).Sum();
|
||||||
if (totalConfirmed >= accounting.TotalDue)
|
if (totalConfirmed >= accounting.TotalDue)
|
||||||
@@ -263,18 +273,23 @@ namespace BTCPayServer.HostedServices
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<IEnumerable<NetworkCoins>> GetCoinsPerNetwork(UpdateInvoiceContext context, InvoiceEntity invoice, DerivationStrategy[] strategies)
|
private IEnumerable<Task<NetworkCoins>> GetCoinsPerNetwork(UpdateInvoiceContext context, InvoiceEntity invoice, DerivationStrategy[] strategies)
|
||||||
{
|
{
|
||||||
var getCoinsResponsesAsync = strategies
|
return strategies
|
||||||
.Select(d => _Wallet.GetCoins(d, context.KnownStates.TryGet(d.Network)))
|
.Select(d => (Wallet: _WalletProvider.GetWallet(d.Network),
|
||||||
|
Network: d.Network,
|
||||||
|
Strategy: d.DerivationStrategyBase))
|
||||||
|
.Where(d => d.Wallet != null)
|
||||||
|
.Select(d => (Network: d.Network,
|
||||||
|
Coins: d.Wallet.GetCoins(d.Strategy, context.KnownStates.TryGet(d.Network))))
|
||||||
|
.Select(async d =>
|
||||||
|
{
|
||||||
|
var coins = await d.Coins;
|
||||||
|
// Keep only coins from the invoice
|
||||||
|
coins.TimestampedCoins = coins.TimestampedCoins.Where(c => invoice.AvailableAddressHashes.Contains(c.Coin.ScriptPubKey.Hash.ToString() + d.Network.CryptoCode)).ToArray();
|
||||||
|
return coins;
|
||||||
|
})
|
||||||
.ToArray();
|
.ToArray();
|
||||||
await Task.WhenAll(getCoinsResponsesAsync);
|
|
||||||
var getCoinsResponses = getCoinsResponsesAsync.Select(g => g.Result).ToArray();
|
|
||||||
foreach (var response in getCoinsResponses)
|
|
||||||
{
|
|
||||||
response.TimestampedCoins = response.TimestampedCoins.Where(c => invoice.AvailableAddressHashes.Contains(c.Coin.ScriptPubKey.Hash.ToString() + response.Strategy.Network.CryptoCode)).ToArray();
|
|
||||||
}
|
|
||||||
return getCoinsResponses.Where(s => s.TimestampedCoins.Length != 0).ToArray();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<IEnumerable<AccountedPaymentEntity>> GetPaymentsWithTransaction(DerivationStrategy[] derivations, InvoiceEntity invoice)
|
private async Task<IEnumerable<AccountedPaymentEntity>> GetPaymentsWithTransaction(DerivationStrategy[] derivations, InvoiceEntity invoice)
|
||||||
@@ -283,7 +298,10 @@ namespace BTCPayServer.HostedServices
|
|||||||
List<AccountedPaymentEntity> accountedPayments = new List<AccountedPaymentEntity>();
|
List<AccountedPaymentEntity> accountedPayments = new List<AccountedPaymentEntity>();
|
||||||
foreach (var network in derivations.Select(d => d.Network))
|
foreach (var network in derivations.Select(d => d.Network))
|
||||||
{
|
{
|
||||||
var transactions = await _Wallet.GetTransactions(network, invoice.GetPayments(network).Select(t => t.Outpoint.Hash).ToArray());
|
var wallet = _WalletProvider.GetWallet(network);
|
||||||
|
if (wallet == null)
|
||||||
|
continue;
|
||||||
|
var transactions = await wallet.GetTransactions(network, invoice.GetPayments(network).Select(t => t.Outpoint.Hash).ToArray());
|
||||||
var conflicts = GetConflicts(transactions.Select(t => t.Value));
|
var conflicts = GetConflicts(transactions.Select(t => t.Value));
|
||||||
foreach (var payment in invoice.GetPayments(network))
|
foreach (var payment in invoice.GetPayments(network))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ namespace BTCPayServer.Hosting
|
|||||||
|
|
||||||
services.TryAddSingleton<NBXplorerDashboard>();
|
services.TryAddSingleton<NBXplorerDashboard>();
|
||||||
services.TryAddSingleton<StoreRepository>();
|
services.TryAddSingleton<StoreRepository>();
|
||||||
services.TryAddSingleton<BTCPayWallet>();
|
services.TryAddSingleton<BTCPayWalletProvider>();
|
||||||
services.TryAddSingleton<CurrencyNameTable>();
|
services.TryAddSingleton<CurrencyNameTable>();
|
||||||
services.TryAddSingleton<IFeeProviderFactory>(o => new NBXplorerFeeProviderFactory(o.GetRequiredService<ExplorerClientProvider>())
|
services.TryAddSingleton<IFeeProviderFactory>(o => new NBXplorerFeeProviderFactory(o.GetRequiredService<ExplorerClientProvider>())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -26,31 +26,40 @@ namespace BTCPayServer.Services.Wallets
|
|||||||
}
|
}
|
||||||
public TimestampedCoin[] TimestampedCoins { get; set; }
|
public TimestampedCoin[] TimestampedCoins { get; set; }
|
||||||
public KnownState State { get; set; }
|
public KnownState State { get; set; }
|
||||||
public DerivationStrategy Strategy { get; set; }
|
public DerivationStrategyBase Strategy { get; set; }
|
||||||
|
public BTCPayWallet Wallet { get; set; }
|
||||||
}
|
}
|
||||||
public class BTCPayWallet
|
public class BTCPayWallet
|
||||||
{
|
{
|
||||||
private ExplorerClientProvider _Client;
|
private ExplorerClient _Client;
|
||||||
|
|
||||||
public BTCPayWallet(ExplorerClientProvider client)
|
public BTCPayWallet(ExplorerClient client, BTCPayNetwork network)
|
||||||
{
|
{
|
||||||
if (client == null)
|
if (client == null)
|
||||||
throw new ArgumentNullException(nameof(client));
|
throw new ArgumentNullException(nameof(client));
|
||||||
_Client = client;
|
_Client = client;
|
||||||
|
_Network = network;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async Task<BitcoinAddress> ReserveAddressAsync(DerivationStrategy derivationStrategy)
|
private readonly BTCPayNetwork _Network;
|
||||||
|
public BTCPayNetwork Network
|
||||||
{
|
{
|
||||||
var client = _Client.GetExplorerClient(derivationStrategy.Network);
|
get
|
||||||
var pathInfo = await client.GetUnusedAsync(derivationStrategy.DerivationStrategyBase, DerivationFeature.Deposit, 0, true).ConfigureAwait(false);
|
{
|
||||||
return pathInfo.ScriptPubKey.GetDestinationAddress(client.Network);
|
return _Network;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task TrackAsync(DerivationStrategy derivationStrategy)
|
public async Task<BitcoinAddress> ReserveAddressAsync(DerivationStrategyBase derivationStrategy)
|
||||||
{
|
{
|
||||||
var client = _Client.GetExplorerClient(derivationStrategy.Network);
|
var pathInfo = await _Client.GetUnusedAsync(derivationStrategy, DerivationFeature.Deposit, 0, true).ConfigureAwait(false);
|
||||||
await client.TrackAsync(derivationStrategy.DerivationStrategyBase);
|
return pathInfo.ScriptPubKey.GetDestinationAddress(_Client.Network);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task TrackAsync(DerivationStrategyBase derivationStrategy)
|
||||||
|
{
|
||||||
|
await _Client.TrackAsync(derivationStrategy);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<TransactionResult> GetTransactionAsync(BTCPayNetwork network, uint256 txId, CancellationToken cancellation = default(CancellationToken))
|
public Task<TransactionResult> GetTransactionAsync(BTCPayNetwork network, uint256 txId, CancellationToken cancellation = default(CancellationToken))
|
||||||
@@ -59,36 +68,31 @@ namespace BTCPayServer.Services.Wallets
|
|||||||
throw new ArgumentNullException(nameof(network));
|
throw new ArgumentNullException(nameof(network));
|
||||||
if (txId == null)
|
if (txId == null)
|
||||||
throw new ArgumentNullException(nameof(txId));
|
throw new ArgumentNullException(nameof(txId));
|
||||||
var client = _Client.GetExplorerClient(network);
|
return _Client.GetTransactionAsync(txId, cancellation);
|
||||||
return client.GetTransactionAsync(txId, cancellation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<NetworkCoins> GetCoins(DerivationStrategy strategy, KnownState state, CancellationToken cancellation = default(CancellationToken))
|
public async Task<NetworkCoins> GetCoins(DerivationStrategyBase strategy, KnownState state, CancellationToken cancellation = default(CancellationToken))
|
||||||
{
|
{
|
||||||
var client = _Client.GetExplorerClient(strategy.Network);
|
var changes = await _Client.SyncAsync(strategy, state?.ConfirmedHash, state?.UnconfirmedHash, true, cancellation).ConfigureAwait(false);
|
||||||
if (client == null)
|
|
||||||
return new NetworkCoins() { TimestampedCoins = new NetworkCoins.TimestampedCoin[0], State = null, Strategy = strategy };
|
|
||||||
var changes = await client.SyncAsync(strategy.DerivationStrategyBase, state?.ConfirmedHash, state?.UnconfirmedHash, true, cancellation).ConfigureAwait(false);
|
|
||||||
return new NetworkCoins()
|
return new NetworkCoins()
|
||||||
{
|
{
|
||||||
TimestampedCoins = changes.Confirmed.UTXOs.Concat(changes.Unconfirmed.UTXOs).Select(c => new NetworkCoins.TimestampedCoin() { Coin = c.AsCoin(), DateTime = c.Timestamp }).ToArray(),
|
TimestampedCoins = changes.Confirmed.UTXOs.Concat(changes.Unconfirmed.UTXOs).Select(c => new NetworkCoins.TimestampedCoin() { Coin = c.AsCoin(), DateTime = c.Timestamp }).ToArray(),
|
||||||
State = new KnownState() { ConfirmedHash = changes.Confirmed.Hash, UnconfirmedHash = changes.Unconfirmed.Hash },
|
State = new KnownState() { ConfirmedHash = changes.Confirmed.Hash, UnconfirmedHash = changes.Unconfirmed.Hash },
|
||||||
Strategy = strategy,
|
Strategy = strategy,
|
||||||
|
Wallet = this
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task BroadcastTransactionsAsync(BTCPayNetwork network, List<Transaction> transactions)
|
public Task BroadcastTransactionsAsync(List<Transaction> transactions)
|
||||||
{
|
{
|
||||||
var client = _Client.GetExplorerClient(network);
|
var tasks = transactions.Select(t => _Client.BroadcastAsync(t)).ToArray();
|
||||||
var tasks = transactions.Select(t => client.BroadcastAsync(t)).ToArray();
|
|
||||||
return Task.WhenAll(tasks);
|
return Task.WhenAll(tasks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async Task<Money> GetBalance(DerivationStrategy derivationStrategy)
|
public async Task<Money> GetBalance(DerivationStrategyBase derivationStrategy)
|
||||||
{
|
{
|
||||||
var client = _Client.GetExplorerClient(derivationStrategy.Network);
|
var result = await _Client.SyncAsync(derivationStrategy, null, true);
|
||||||
var result = await client.SyncAsync(derivationStrategy.DerivationStrategyBase, null, true);
|
|
||||||
return result.Confirmed.UTXOs.Select(u => u.Value)
|
return result.Confirmed.UTXOs.Select(u => u.Value)
|
||||||
.Concat(result.Unconfirmed.UTXOs.Select(u => u.Value))
|
.Concat(result.Unconfirmed.UTXOs.Select(u => u.Value))
|
||||||
.Sum();
|
.Sum();
|
||||||
|
|||||||
37
BTCPayServer/Services/Wallets/BTCPayWalletProvider.cs
Normal file
37
BTCPayServer/Services/Wallets/BTCPayWalletProvider.cs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace BTCPayServer.Services.Wallets
|
||||||
|
{
|
||||||
|
public class BTCPayWalletProvider
|
||||||
|
{
|
||||||
|
private ExplorerClientProvider _Client;
|
||||||
|
BTCPayNetworkProvider _NetworkProvider;
|
||||||
|
public BTCPayWalletProvider(ExplorerClientProvider client, BTCPayNetworkProvider networkProvider)
|
||||||
|
{
|
||||||
|
if (client == null)
|
||||||
|
throw new ArgumentNullException(nameof(client));
|
||||||
|
_Client = client;
|
||||||
|
_NetworkProvider = networkProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BTCPayWallet GetWallet(BTCPayNetwork network)
|
||||||
|
{
|
||||||
|
if (network == null)
|
||||||
|
throw new ArgumentNullException(nameof(network));
|
||||||
|
return GetWallet(network.CryptoCode);
|
||||||
|
}
|
||||||
|
public BTCPayWallet GetWallet(string cryptoCode)
|
||||||
|
{
|
||||||
|
if (cryptoCode == null)
|
||||||
|
throw new ArgumentNullException(nameof(cryptoCode));
|
||||||
|
var network = _NetworkProvider.GetNetwork(cryptoCode);
|
||||||
|
var client = _Client.GetExplorerClient(cryptoCode);
|
||||||
|
if (network == null && client == null)
|
||||||
|
return null;
|
||||||
|
return new BTCPayWallet(client, network);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user