using System; using System.Linq; using System.Net.Http; using System.Threading; using BTCPayServer.Abstractions.Contracts; using BTCPayServer.Abstractions.Extensions; using BTCPayServer.Abstractions.Models; using BTCPayServer.Abstractions.Services; using BTCPayServer.Client; using BTCPayServer.Common; using BTCPayServer.Configuration; using BTCPayServer.Controllers; using BTCPayServer.Controllers.Greenfield; using BTCPayServer.Data; using BTCPayServer.Data.Payouts.LightningLike; using BTCPayServer.Forms; using BTCPayServer.HostedServices; using BTCPayServer.HostedServices.Webhooks; using BTCPayServer.Lightning; using BTCPayServer.Lightning.Charge; using BTCPayServer.Lightning.CLightning; using BTCPayServer.Lightning.Eclair; using BTCPayServer.Lightning.LNbank; using BTCPayServer.Lightning.LND; using BTCPayServer.Lightning.LNDhub; using BTCPayServer.Logging; using BTCPayServer.PaymentRequest; using BTCPayServer.Payments; using BTCPayServer.Payments.Bitcoin; using BTCPayServer.Payments.Lightning; using BTCPayServer.Payments.PayJoin; using BTCPayServer.PayoutProcessors; using BTCPayServer.Plugins; using BTCPayServer.Rating; using BTCPayServer.Rating.Providers; using BTCPayServer.Security; using BTCPayServer.Security.Bitpay; using BTCPayServer.Security.Greenfield; using BTCPayServer.Services; using BTCPayServer.Services.Apps; using BTCPayServer.Services.Fees; using BTCPayServer.Services.Invoices; using BTCPayServer.Services.Labels; using BTCPayServer.Services.Mails; using BTCPayServer.Services.Notifications; using BTCPayServer.Services.Notifications.Blobs; using BTCPayServer.Services.PaymentRequests; using BTCPayServer.Services.Rates; using BTCPayServer.Services.Stores; using BTCPayServer.Services.Wallets; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using NBitcoin; using NBitpayClient; using NBXplorer.DerivationStrategy; using Newtonsoft.Json; using Serilog; using BTCPayServer.Services.Reporting; using BTCPayServer.Services.WalletFileParsing; using BTCPayServer.Payments.LNURLPay; using System.Collections.Generic; using BTCPayServer.Payouts; using ExchangeSharp; #if ALTCOINS using BTCPayServer.Services.Altcoins.Monero; using BTCPayServer.Services.Altcoins.Zcash; #endif namespace BTCPayServer.Hosting { public static class BTCPayServerServices { public static IServiceCollection RegisterJsonConverter(this IServiceCollection services, Func create) { services.AddSingleton((s) => new JsonConverterRegistration(create)); return services; } public static IServiceCollection AddBTCPayServer(this IServiceCollection services, IConfiguration configuration, Logs logs) { services.AddSingleton(o => o.GetRequiredService>().Value); services.AddSingleton(o => o.GetRequiredService>().Value.SerializerSettings); services.AddDbContext((provider, o) => { var factory = provider.GetRequiredService(); factory.ConfigureBuilder(o); }); services.AddHttpClient(); services.AddHttpClient(nameof(ExplorerClientProvider), httpClient => { httpClient.Timeout = Timeout.InfiniteTimeSpan; }); services.AddHttpClient((prov, httpClient) => { var p = prov.GetRequiredService(); var pluginSource = p.PluginSource ?? PoliciesSettings.DefaultPluginSource; if (pluginSource.EndsWith('/')) pluginSource = pluginSource.Substring(0, pluginSource.Length - 1); if (!Uri.TryCreate(pluginSource, UriKind.Absolute, out var r) || (r.Scheme != "https" && r.Scheme != "http")) r = new Uri(PoliciesSettings.DefaultPluginSource, UriKind.Absolute); httpClient.BaseAddress = r; }); services.AddSingleton(); services.AddSingleton(logs); services.AddSingleton(); services.AddPayJoinServices(); services.AddScoped(); services.TryAddSingleton(); services.TryAddSingleton(provider => provider.GetService()); services.TryAddSingleton(provider => provider.GetService()); services.TryAddSingleton(); services.AddSingleton(provider => provider.GetRequiredService()); services.AddSingleton(); services.TryAddSingleton(); services.AddSingleton>(client => new ChargeLightningConnectionStringHandler(client)); services.AddSingleton>(_ => new CLightningConnectionStringHandler()); services.AddSingleton>(client => new EclairConnectionStringHandler(client)); services.AddSingleton>(client => new LndConnectionStringHandler(client)); services.AddSingleton>(client => new LndHubConnectionStringHandler(client)); services.AddSingleton>(client => new LNbankConnectionStringHandler(client)); services.TryAddSingleton(); services.AddHttpClient(LightningClientFactoryService.OnionNamedClient) .ConfigurePrimaryHttpMessageHandler(); services.TryAddSingleton(); services.TryAddSingleton(o => o.GetRequiredService>().Value); services.AddStartupTask(); // AddSettingsAccessor(services); AddSettingsAccessor(services); // AddOnchainWalletParsers(services); services.AddSingleton(new UIExtension("Bitcoin/ViewBitcoinLikePaymentData", "store-invoices-payments")); services.AddSingleton(new UIExtension("Lightning/ViewLightningLikePaymentData", "store-invoices-payments")); services.AddStartupTask(); services.TryAddSingleton(); services.AddSingleton(); services.AddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); services.AddSingleton(); services.AddOptions().Configure( (options) => { options.LoadArgs(configuration, logs); }); services.AddOptions().Configure( (options) => { options.Configure(configuration); }); services.AddOptions().Configure>( (options, datadirs) => { var postgresConnectionString = configuration["postgres"]; if (!string.IsNullOrEmpty(postgresConnectionString)) { options.ConnectionString = postgresConnectionString; } else { throw new InvalidOperationException("No database option was configured."); } }); services.AddOptions().Configure( (options, btcPayNetworkProvider) => { foreach (BTCPayNetwork btcPayNetwork in btcPayNetworkProvider.GetAll().OfType()) { NBXplorerConnectionSetting setting = new NBXplorerConnectionSetting { CryptoCode = btcPayNetwork.CryptoCode, ExplorerUri = configuration.GetOrDefault( $"{btcPayNetwork.CryptoCode}.explorer.url", btcPayNetwork.NBXplorerNetwork.DefaultSettings.DefaultUrl), CookieFile = configuration.GetOrDefault( $"{btcPayNetwork.CryptoCode}.explorer.cookiefile", btcPayNetwork.NBXplorerNetwork.DefaultSettings.DefaultCookieFile) }; options.NBXplorerConnectionSettings.Add(setting); options.ConnectionString = configuration.GetOrDefault("explorer.postgres", null); } }); services.AddOptions().Configure( (options, btcPayNetworkProvider, lightningClientFactoryService) => { foreach (var net in btcPayNetworkProvider.GetAll().OfType()) { var lightning = configuration.GetOrDefault($"{net.CryptoCode}.lightning", string.Empty); if (lightning.Length != 0) { string error = null; ILightningClient lightningClient = null; try { lightningClient = lightningClientFactoryService.Create(lightning, net); } catch (Exception e) { error = e.Message; } if (error is not null) { logs.Configuration.LogWarning($"Invalid setting {net.CryptoCode}.lightning, " + Environment.NewLine + $"If you have a c-lightning server use: 'type=clightning;server=/root/.lightning/lightning-rpc', " + Environment.NewLine + $"If you have a lightning charge server: 'type=charge;server=https://charge.example.com;api-token=yourapitoken'" + Environment.NewLine + $"If you have a lnd server: 'type=lnd-rest;server=https://lnd:lnd@lnd.example.com;macaroon=abf239...;certthumbprint=2abdf302...'" + Environment.NewLine + $" lnd server: 'type=lnd-rest;server=https://lnd:lnd@lnd.example.com;macaroonfilepath=/root/.lnd/admin.macaroon;certthumbprint=2abdf302...'" + Environment.NewLine + $"If you have an eclair server: 'type=eclair;server=http://eclair.com:4570;password=eclairpassword;bitcoin-host=bitcoind:37393;bitcoin-auth=bitcoinrpcuser:bitcoinrpcpassword" + Environment.NewLine + $" eclair server: 'type=eclair;server=http://eclair.com:4570;password=eclairpassword;bitcoin-host=bitcoind:37393" + Environment.NewLine + $"Error: {error}" + Environment.NewLine + "This service will not be exposed through BTCPay Server"); } else { if (lightningClient.ToString() != lightning) { logs.Configuration.LogWarning( $"Setting {net.CryptoCode}.lightning is a deprecated format ({lightning}), it will work now, but please replace it for future versions with '{lightningClient}'"); } options.InternalLightningByCryptoCode.Add(net.CryptoCode, lightningClient); } } } }); services.AddOptions().Configure( (options, btcPayNetworkProvider) => { foreach (var net in btcPayNetworkProvider.GetAll().OfType()) { options.ExternalServices.Load(net.CryptoCode, configuration); } options.ExternalServices.LoadNonCryptoServices(configuration); var services = configuration.GetOrDefault("externalservices", null); if (services != null) { foreach (var service in services.Split(new[] { ';', ',' }, StringSplitOptions.RemoveEmptyEntries) .Select(p => (p, SeparatorIndex: p.IndexOf(':', StringComparison.OrdinalIgnoreCase))) .Where(p => p.SeparatorIndex != -1) .Select(p => (Name: p.p.Substring(0, p.SeparatorIndex), Link: p.p.Substring(p.SeparatorIndex + 1)))) { if (Uri.TryCreate(service.Link, UriKind.RelativeOrAbsolute, out var uri)) options.OtherExternalServices.AddOrReplace(service.Name, uri); } } }); services.TryAddSingleton(); services.AddExceptionHandler(); services.TryAddSingleton(); services.AddTransient(); services.AddSingleton(); services.AddSingleton(provider => provider.GetService()); services.TryAddTransient(); services.TryAddTransient(); services.TryAddSingleton(o => { var htmlSanitizer = new Ganss.Xss.HtmlSanitizer(); htmlSanitizer.RemovingAtRule += (sender, args) => { }; htmlSanitizer.RemovingTag += (sender, args) => { if (args.Tag.TagName.Equals("img", StringComparison.InvariantCultureIgnoreCase)) { if (!args.Tag.ClassList.Contains("img-fluid")) { args.Tag.ClassList.Add("img-fluid"); } args.Cancel = true; } }; htmlSanitizer.RemovingAttribute += (sender, args) => { if (args.Tag.TagName.Equals("img", StringComparison.InvariantCultureIgnoreCase) && args.Attribute.Name.Equals("src", StringComparison.InvariantCultureIgnoreCase) && args.Reason == Ganss.Xss.RemoveReason.NotAllowedUrlValue) { args.Cancel = true; } }; htmlSanitizer.RemovingStyle += (sender, args) => { args.Cancel = true; }; htmlSanitizer.AllowedAttributes.Add("class"); htmlSanitizer.AllowedTags.Add("iframe"); htmlSanitizer.AllowedTags.Add("style"); htmlSanitizer.AllowedTags.Remove("img"); htmlSanitizer.AllowedAttributes.Add("webkitallowfullscreen"); htmlSanitizer.AllowedAttributes.Add("allowfullscreen"); htmlSanitizer.AllowedSchemes.Add("mailto"); htmlSanitizer.AllowedSchemes.Add("bitcoin"); htmlSanitizer.AllowedSchemes.Add("lightning"); return htmlSanitizer; }); services.AddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); services.AddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); services.AddSingleton(provider => provider.GetService()); services.TryAddSingleton(CurrencyNameTable.Instance); services.AddScheduledTask(TimeSpan.FromMinutes(3.0)); services.AddSingleton(f => f.GetRequiredService()); services.Configure((o) => { o.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(WalletId))); o.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(DerivationStrategyBase))); }); services.AddSingleton(); services.AddSingleton(o => o.GetRequiredService()); services.AddSingleton(); services.AddSingleton(o => o.GetRequiredService()); services.AddSingleton(); services.AddSingleton(); services.AddScheduledTask(TimeSpan.FromDays(1)); services.AddScheduledTask(TimeSpan.FromDays(1)); services.AddReportProvider(); services.AddReportProvider(); services.AddReportProvider(); services.AddReportProvider(); services.AddReportProvider(); services.AddReportProvider(); services.AddWebhooks(); services.AddSingleton>(o => o.GetRequiredService>().ToDictionary(o => o.PaymentMethodId, o => o)); services.AddSingleton>(o => o.GetRequiredService>().ToDictionary(o => o.PaymentMethodId, o => o)); services.AddSingleton>(o => o.GetRequiredService>().ToDictionary(o => o.PaymentMethodId, o => o)); services.AddHttpClient(LightningLikePayoutHandler.LightningLikePayoutHandlerOnionNamedClient) .ConfigurePrimaryHttpMessageHandler(); services.AddSingleton(); services.AddSingleton(o => o.GetRequiredService()); services.AddSingleton(); services.AddSingleton(new UIExtension("LNURL/LightningAddressNav", "store-integrations-nav")); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddScoped(); RegisterExchangeRecommendations(services); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddScoped(); services.AddScoped(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.TryAddSingleton(); services.AddSingleton(x => x.GetRequiredService()); services.TryAddSingleton(o => { if (o.GetRequiredService().NetworkType == ChainName.Mainnet) return new Bitpay(new Key(), new Uri("https://bitpay.com/")); else return new Bitpay(new Key(), new Uri("https://test.bitpay.com/")); }); RegisterRateSources(services); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddScoped(); services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddSingleton(); // Add application services. services.AddSingleton(); services.AddSingleton(); //create a simple client which hooks up to the http scope services.AddScoped(); //also provide a factory that can impersonate user/store id services.AddSingleton(); services.AddPayoutProcesors(); services.AddForms(); services.AddAPIKeyAuthentication(); services.AddBtcPayServerAuthenticationSchemes(); services.AddAuthorization(o => o.AddBTCPayPolicies()); services.AddCors(options => { options.AddPolicy(CorsPolicies.All, p => p.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin()); }); services.AddRateLimits(); services.AddLogging(logBuilder => { var debugLogFile = BTCPayServerOptions.GetDebugLog(configuration); if (!string.IsNullOrEmpty(debugLogFile)) { Serilog.Log.Logger = new LoggerConfiguration() .Enrich.FromLogContext() .MinimumLevel.Is(BTCPayServerOptions.GetDebugLogLevel(configuration)) .WriteTo.File(debugLogFile, rollingInterval: RollingInterval.Day, fileSizeLimitBytes: MAX_DEBUG_LOG_FILE_SIZE, rollOnFileSizeLimit: true, retainedFileCountLimit: 1) .CreateLogger(); logBuilder.AddProvider(new Serilog.Extensions.Logging.SerilogLoggerProvider(Log.Logger)); } }); services.AddSingleton(); services.SkipModelValidation(); services.SkipModelValidation(); if (configuration.GetOrDefault("cheatmode", false)) { services.AddSingleton(); services.AddSingleton(o => o.GetRequiredService()); } var userAgent = new System.Net.Http.Headers.ProductInfoHeaderValue("BTCPayServer", BTCPayServerEnvironment.GetInformationalVersion()); foreach (var clientName in WebhookSender.AllClients.Concat(new[] { BitpayIPNSender.NamedClient })) { services.AddHttpClient(clientName) .ConfigureHttpClient(client => { client.DefaultRequestHeaders.UserAgent.Add(userAgent); }); } return services; } public static void RegisterExchangeRecommendations(IServiceCollection services) { foreach (var rule in new Dictionary() { { "EUR", "kraken" }, { "USD", "kraken" }, { "GBP", "kraken" }, { "CHF", "kraken" }, { "GTQ", "bitpay" }, { "COP", "yadio" }, { "ARS", "yadio" }, { "JPY", "bitbank" }, { "TRY", "btcturk" }, { "UGX", "yadio"}, { "RSD", "bitpay"}, { "NGN", "bitnob"} }) { var r = new DefaultRules.Recommendation(rule.Key, rule.Value); r.Order = DefaultRules.HardcodedRecommendedExchangeOrder; services.AddSingleton(r); } } public static void AddOnchainWalletParsers(IServiceCollection services) { services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(provider => provider.GetService()); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); } internal static void RegisterRateSources(IServiceCollection services) { // We need to be careful to only add exchanges which OnGetTickers implementation make only 1 request services.AddRateProviderExchangeSharp(new("binance", "Binance", "https://api.binance.com/api/v1/ticker/24hr")); services.AddRateProviderExchangeSharp(new("poloniex", "Poloniex", " https://api.poloniex.com/markets/price")); services.AddRateProviderExchangeSharp(new("ndax", "NDAX", "https://ndax.io/api/returnTicker")); services.AddRateProviderExchangeSharp(new("bitfinex", "Bitfinex", "https://api.bitfinex.com/v2/tickers?symbols=tBTCUSD,tLTCUSD,tLTCBTC,tETHUSD,tETHBTC,tETCBTC,tETCUSD,tRRTUSD,tRRTBTC,tZECUSD,tZECBTC,tXMRUSD,tXMRBTC,tDSHUSD,tDSHBTC,tBTCEUR,tBTCJPY,tXRPUSD,tXRPBTC,tIOTUSD,tIOTBTC,tIOTETH,tEOSUSD,tEOSBTC,tEOSETH,tSANUSD,tSANBTC,tSANETH,tOMGUSD,tOMGBTC,tOMGETH,tNEOUSD,tNEOBTC,tNEOETH,tETPUSD,tETPBTC,tETPETH,tQTMUSD,tQTMBTC,tQTMETH,tAVTUSD,tAVTBTC,tAVTETH,tEDOUSD,tEDOBTC,tEDOETH,tBTGUSD,tBTGBTC,tDATUSD,tDATBTC,tDATETH,tQSHUSD,tQSHBTC,tQSHETH,tYYWUSD,tYYWBTC,tYYWETH,tGNTUSD,tGNTBTC,tGNTETH,tSNTUSD,tSNTBTC,tSNTETH,tIOTEUR,tBATUSD,tBATBTC,tBATETH,tMNAUSD,tMNABTC,tMNAETH,tFUNUSD,tFUNBTC,tFUNETH,tZRXUSD,tZRXBTC,tZRXETH,tTNBUSD,tTNBBTC,tTNBETH,tSPKUSD,tSPKBTC,tSPKETH,tTRXUSD,tTRXBTC,tTRXETH,tRCNUSD,tRCNBTC,tRCNETH,tRLCUSD,tRLCBTC,tRLCETH,tAIDUSD,tAIDBTC,tAIDETH,tSNGUSD,tSNGBTC,tSNGETH,tREPUSD,tREPBTC,tREPETH,tELFUSD,tELFBTC,tELFETH,tNECUSD,tNECBTC,tNECETH,tBTCGBP,tETHEUR,tETHJPY,tETHGBP,tNEOEUR,tNEOJPY,tNEOGBP,tEOSEUR,tEOSJPY,tEOSGBP,tIOTJPY,tIOTGBP,tIOSUSD,tIOSBTC,tIOSETH,tAIOUSD,tAIOBTC,tAIOETH,tREQUSD,tREQBTC,tREQETH,tRDNUSD,tRDNBTC,tRDNETH,tLRCUSD,tLRCBTC,tLRCETH,tWAXUSD,tWAXBTC,tWAXETH,tDAIUSD,tDAIBTC,tDAIETH,tAGIUSD,tAGIBTC,tAGIETH,tBFTUSD,tBFTBTC,tBFTETH,tMTNUSD,tMTNBTC,tMTNETH,tODEUSD,tODEBTC,tODEETH,tANTUSD,tANTBTC,tANTETH,tDTHUSD,tDTHBTC,tDTHETH,tMITUSD,tMITBTC,tMITETH,tSTJUSD,tSTJBTC,tSTJETH,tXLMUSD,tXLMEUR,tXLMJPY,tXLMGBP,tXLMBTC,tXLMETH,tXVGUSD,tXVGEUR,tXVGJPY,tXVGGBP,tXVGBTC,tXVGETH,tBCIUSD,tBCIBTC,tMKRUSD,tMKRBTC,tMKRETH,tKNCUSD,tKNCBTC,tKNCETH,tPOAUSD,tPOABTC,tPOAETH,tEVTUSD,tLYMUSD,tLYMBTC,tLYMETH,tUTKUSD,tUTKBTC,tUTKETH,tVEEUSD,tVEEBTC,tVEEETH,tDADUSD,tDADBTC,tDADETH,tORSUSD,tORSBTC,tORSETH,tAUCUSD,tAUCBTC,tAUCETH,tPOYUSD,tPOYBTC,tPOYETH,tFSNUSD,tFSNBTC,tFSNETH,tCBTUSD,tCBTBTC,tCBTETH,tZCNUSD,tZCNBTC,tZCNETH,tSENUSD,tSENBTC,tSENETH,tNCAUSD,tNCABTC,tNCAETH,tCNDUSD,tCNDBTC,tCNDETH,tCTXUSD,tCTXBTC,tCTXETH,tPAIUSD,tPAIBTC,tSEEUSD,tSEEBTC,tSEEETH,tESSUSD,tESSBTC,tESSETH,tATMUSD,tATMBTC,tATMETH,tHOTUSD,tHOTBTC,tHOTETH,tDTAUSD,tDTABTC,tDTAETH,tIQXUSD,tIQXBTC,tIQXEOS,tWPRUSD,tWPRBTC,tWPRETH,tZILUSD,tZILBTC,tZILETH,tBNTUSD,tBNTBTC,tBNTETH,tABSUSD,tABSETH,tXRAUSD,tXRAETH,tMANUSD,tMANETH,tBBNUSD,tBBNETH,tNIOUSD,tNIOETH,tDGXUSD,tDGXETH,tVETUSD,tVETBTC,tVETETH,tUTNUSD,tUTNETH,tTKNUSD,tTKNETH,tGOTUSD,tGOTEUR,tGOTETH,tXTZUSD,tXTZBTC,tCNNUSD,tCNNETH,tBOXUSD,tBOXETH,tTRXEUR,tTRXGBP,tTRXJPY,tMGOUSD,tMGOETH,tRTEUSD,tRTEETH,tYGGUSD,tYGGETH,tMLNUSD,tMLNETH,tWTCUSD,tWTCETH,tCSXUSD,tCSXETH,tOMNUSD,tOMNBTC,tINTUSD,tINTETH,tDRNUSD,tDRNETH,tPNKUSD,tPNKETH,tDGBUSD,tDGBBTC,tBSVUSD,tBSVBTC,tBABUSD,tBABBTC,tWLOUSD,tWLOXLM,tVLDUSD,tVLDETH,tENJUSD,tENJETH,tONLUSD,tONLETH,tRBTUSD,tRBTBTC,tUSTUSD,tEUTEUR,tEUTUSD,tGSDUSD,tUDCUSD,tTSDUSD,tPAXUSD,tRIFUSD,tRIFBTC,tPASUSD,tPASETH,tVSYUSD,tVSYBTC,tZRXDAI,tMKRDAI,tOMGDAI,tBTTUSD,tBTTBTC,tBTCUST,tETHUST,tCLOUSD,tCLOBTC,tIMPUSD,tIMPETH,tLTCUST,tEOSUST,tBABUST,tSCRUSD,tSCRETH,tGNOUSD,tGNOETH,tGENUSD,tGENETH,tATOUSD,tATOBTC,tATOETH,tWBTUSD,tXCHUSD,tEUSUSD,tWBTETH,tXCHETH,tEUSETH,tLEOUSD,tLEOBTC,tLEOUST,tLEOEOS,tLEOETH,tASTUSD,tASTETH,tFOAUSD,tFOAETH,tUFRUSD,tUFRETH,tZBTUSD,tZBTUST,tOKBUSD,tUSKUSD,tGTXUSD,tKANUSD,tOKBUST,tOKBETH,tOKBBTC,tUSKUST,tUSKETH,tUSKBTC,tUSKEOS,tGTXUST,tKANUST,tAMPUSD,tALGUSD,tALGBTC,tALGUST,tBTCXCH,tSWMUSD,tSWMETH,tTRIUSD,tTRIETH,tLOOUSD,tLOOETH,tAMPUST,tDUSK:USD,tDUSK:BTC,tUOSUSD,tUOSBTC,tRRBUSD,tRRBUST,tDTXUSD,tDTXUST,tAMPBTC,tFTTUSD,tFTTUST,tPAXUST,tUDCUST,tTSDUST,tBTC:CNHT,tUST:CNHT,tCNH:CNHT,tCHZUSD,tCHZUST,tBTCF0:USTF0,tETHF0:USTF0")); services.AddRateProviderExchangeSharp(new("okex", "OKEx", "https://www.okex.com/api/futures/v3/instruments/ticker")); services.AddRateProviderExchangeSharp(new("coinbasepro", "Coinbase Pro", "https://api.pro.coinbase.com/products")); // Handmade providers services.AddRateProvider(); services.AddRateProvider(); services.AddRateProvider(); services.AddRateProvider(); services.AddRateProvider(); services.AddRateProvider(); services.AddRateProvider(); services.AddRateProvider(); services.AddRateProvider(); services.AddRateProvider(); services.AddRateProvider(); services.AddRateProvider(); services.AddRateProvider(); services.AddRateProvider(); services.AddSingleton(); services.AddSingleton(o => o.GetRequiredService()); services.AddSingleton(); services.AddSingleton(o => o.GetRequiredService()); // Broken // Providers.Add("argoneum", new ArgoneumRateProvider(_httpClientFactory?.CreateClient("EXCHANGE_ARGONEUM"))); // Those exchanges make too many requests, exchange sharp do not parallelize so it is too slow... //AddExchangeSharpProviders("gemini"); //AddExchangeSharpProviders("bitstamp"); //AddExchangeSharpProviders("bitmex"); } public static void AddRateProvider(this IServiceCollection services) where T : class, IRateProvider { services.AddSingleton(); } public static IServiceCollection AddBTCPayNetwork(this IServiceCollection services, BTCPayNetworkBase network) { services.AddSingleton(new DefaultRules(network.DefaultRateRules)); services.AddSingleton(network); return services; } public static IServiceCollection AddBTCPayNetwork(this IServiceCollection services, BTCPayNetwork network) { services.AddSingleton(new DefaultRules(network.DefaultRateRules)); // BTC { var pmi = PaymentTypes.CHAIN.GetPaymentMethodId(network.CryptoCode); services.AddSingleton(network); services.AddSingleton(provider => (BitcoinLikePaymentHandler)ActivatorUtilities.CreateInstance(provider, typeof(BitcoinLikePaymentHandler), new object[] { network, pmi })); services.AddSingleton(provider => (IPaymentLinkExtension)ActivatorUtilities.CreateInstance(provider, typeof(BitcoinPaymentLinkExtension), new object[] { network, pmi })); services.AddSingleton(provider => (BitcoinPaymentModelExtension)ActivatorUtilities.CreateInstance(provider, typeof(BitcoinPaymentModelExtension), new object[] { network, pmi })); services.AddSingleton(provider => (IPaymentMethodBitpayAPIExtension)ActivatorUtilities.CreateInstance(provider, typeof(BitcoinPaymentMethodBitpayAPIExtension), new object[] { pmi })); services.AddSingleton(provider => (IPaymentMethodViewExtension)ActivatorUtilities.CreateInstance(provider, typeof(BitcoinPaymentMethodViewExtension), new object[] { pmi })); if (!network.ReadonlyWallet && network.WalletSupported) { var payoutMethodId = PayoutTypes.CHAIN.GetPayoutMethodId(network.CryptoCode); services.AddSingleton(provider => (IPayoutHandler)ActivatorUtilities.CreateInstance(provider, typeof(BitcoinLikePayoutHandler), new object[] { payoutMethodId, network })); } } if (network.NBitcoinNetwork.Consensus.SupportSegwit && network.SupportLightning) { // LN { var pmi = PaymentTypes.LN.GetPaymentMethodId(network.CryptoCode); services.AddSingleton(provider => (LightningLikePaymentHandler)ActivatorUtilities.CreateInstance(provider, typeof(LightningLikePaymentHandler), new object[] { network, pmi })); services.AddSingleton(provider => (IPaymentLinkExtension)ActivatorUtilities.CreateInstance(provider, typeof(LightningPaymentLinkExtension), new object[] { network, pmi })); services.AddSingleton(provider => (IPaymentModelExtension)ActivatorUtilities.CreateInstance(provider, typeof(LightningPaymentModelExtension), new object[] { network, pmi })); services.AddSingleton(provider => (IPaymentMethodViewExtension)ActivatorUtilities.CreateInstance(provider, typeof(LightningPaymentMethodViewExtension), new object[] { pmi })); services.AddSingleton(provider => (IPaymentMethodBitpayAPIExtension)ActivatorUtilities.CreateInstance(provider, typeof(LightningPaymentMethodBitpayAPIExtension), new object[] { pmi })); var payoutMethodId = PayoutTypes.LN.GetPayoutMethodId(network.CryptoCode); services.AddSingleton(provider => (IPayoutHandler)ActivatorUtilities.CreateInstance(provider, typeof(LightningLikePayoutHandler), new object[] { payoutMethodId, network })); } // LNURL { var pmi = PaymentTypes.LNURL.GetPaymentMethodId(network.CryptoCode); services.AddSingleton(provider => (LNURLPayPaymentHandler)ActivatorUtilities.CreateInstance(provider, typeof(LNURLPayPaymentHandler), new object[] { network, pmi })); services.AddSingleton(provider => (IPaymentLinkExtension)ActivatorUtilities.CreateInstance(provider, typeof(LNURLPayPaymentLinkExtension), new object[] { network, pmi })); services.AddSingleton(provider => (IPaymentModelExtension)ActivatorUtilities.CreateInstance(provider, typeof(LNURLPayPaymentModelExtension), new object[] { network, pmi })); services.AddSingleton(provider => (IPaymentMethodViewExtension)ActivatorUtilities.CreateInstance(provider, typeof(LNURLPaymentMethodViewExtension), new object[] { pmi })); services.AddSingleton(provider => (IPaymentMethodBitpayAPIExtension)ActivatorUtilities.CreateInstance(provider, typeof(LNURLPayPaymentMethodBitpayAPIExtension), new object[] { pmi })); } } return services; } public static void AddTransactionLinkProvider(this IServiceCollection services, string cryptoCode, TransactionLinkProvider provider) { services.AddSingleton(new TransactionLinkProviders.Entry(cryptoCode, provider)); } public static void AddRateProviderExchangeSharp(this IServiceCollection services, RateSourceInfo rateInfo) where T : ExchangeSharp.ExchangeAPI { services.AddSingleton>(o => { var instance = ActivatorUtilities.CreateInstance>(o); instance.RateSourceInfo = rateInfo; return instance; }); } private static void AddSettingsAccessor(IServiceCollection services) where T : class, new() { services.TryAddSingleton, SettingsAccessor>(); services.AddSingleton(provider => (SettingsAccessor)provider.GetRequiredService>()); services.AddSingleton(provider => (SettingsAccessor)provider.GetRequiredService>()); // Singletons shouldn't reference the settings directly, but ISettingsAccessor, since singletons won't have refreshed values of the setting services.AddTransient(provider => provider.GetRequiredService>().Settings); } public static void SkipModelValidation(this IServiceCollection services) { services.AddSingleton>(); } private const long MAX_DEBUG_LOG_FILE_SIZE = 2000000; // If debug log is in use roll it every N MB. private static void AddBtcPayServerAuthenticationSchemes(this IServiceCollection services) { services.AddAuthentication() .AddBitpayAuthentication() .AddAPIKeyAuthentication(); } public static IApplicationBuilder UsePayServer(this IApplicationBuilder app) { app.UseMiddleware(); app.UseMiddleware(); return app; } public static IApplicationBuilder UseHeadersOverride(this IApplicationBuilder app) { app.UseMiddleware(); return app; } } }