diff --git a/CryptoExchange.Net/BaseClient.cs b/CryptoExchange.Net/BaseClient.cs
index 7460bb5..43ca4c9 100644
--- a/CryptoExchange.Net/BaseClient.cs
+++ b/CryptoExchange.Net/BaseClient.cs
@@ -26,10 +26,6 @@ namespace CryptoExchange.Net
///
protected internal Log log;
///
- /// The authentication provider when api credentials have been provided
- ///
- protected internal AuthenticationProvider? authProvider;
- ///
/// The last used id, use NextId() to get the next id and up this
///
protected static int lastId;
@@ -57,11 +53,9 @@ namespace CryptoExchange.Net
///
/// The name of the exchange this client is for
/// The options for this client
- /// The authentication provider for this client (can be null if no credentials are provided)
- protected BaseClient(string exchangeName, ClientOptions options, AuthenticationProvider? authenticationProvider)
+ protected BaseClient(string exchangeName, ClientOptions options)
{
log = new Log(exchangeName);
- authProvider = authenticationProvider;
log.UpdateWriters(options.LogWriters);
log.Level = options.LogLevel;
@@ -72,16 +66,6 @@ namespace CryptoExchange.Net
log.Write(LogLevel.Debug, $"Client configuration: {options}, CryptoExchange.Net: v{typeof(BaseClient).Assembly.GetName().Version}, {ExchangeName}.Net: v{GetType().Assembly.GetName().Version}");
}
- ///
- /// Set the authentication provider, can be used when manually setting the API credentials
- ///
- ///
- protected void SetAuthenticationProvider(AuthenticationProvider authenticationProvider)
- {
- log.Write(LogLevel.Debug, "Setting api credentials");
- authProvider = authenticationProvider;
- }
-
///
/// Tries to parse the json data and return a JToken, validating the input not being empty and being valid json
///
@@ -273,32 +257,13 @@ namespace CryptoExchange.Net
}
}
- ///
- /// Fill parameters in a path. Parameters are specified by '{}' and should be specified in occuring sequence
- ///
- /// The total path string
- /// The values to fill
- ///
- protected static string FillPathParameter(string path, params string[] values)
- {
- foreach (var value in values)
- {
- var index = path.IndexOf("{}", StringComparison.Ordinal);
- if (index >= 0)
- {
- path = path.Remove(index, 2);
- path = path.Insert(index, value);
- }
- }
- return path;
- }
-
///
/// Dispose
///
public virtual void Dispose()
{
- authProvider?.Credentials?.Dispose();
+ // TODO
+ //authProvider?.Credentials?.Dispose();
log.Write(LogLevel.Debug, "Disposing exchange client");
}
}
diff --git a/CryptoExchange.Net/Converters/DateTimeConverter.cs b/CryptoExchange.Net/Converters/DateTimeConverter.cs
index f2edcb6..4d471e2 100644
--- a/CryptoExchange.Net/Converters/DateTimeConverter.cs
+++ b/CryptoExchange.Net/Converters/DateTimeConverter.cs
@@ -85,7 +85,12 @@ namespace CryptoExchange.Net.Converters
// Parse 1637745563.000 format
if (doubleValue < 1999999999)
return ConvertFromSeconds(doubleValue);
- return ConvertFromMilliseconds(doubleValue);
+ if (doubleValue < 1999999999999)
+ return ConvertFromMilliseconds((long)doubleValue);
+ if (doubleValue < 1999999999999999)
+ return ConvertFromMicroseconds((long)doubleValue);
+
+ return ConvertFromNanoseconds((long)doubleValue);
}
if(stringValue.Length == 10)
diff --git a/CryptoExchange.Net/ExtensionMethods.cs b/CryptoExchange.Net/ExtensionMethods.cs
index 26e0418..55a265e 100644
--- a/CryptoExchange.Net/ExtensionMethods.cs
+++ b/CryptoExchange.Net/ExtensionMethods.cs
@@ -72,7 +72,7 @@ namespace CryptoExchange.Net
///
public static void AddOptionalParameter(this Dictionary parameters, string key, object? value)
{
- if(value != null)
+ if (value != null)
parameters.Add(key, value);
}
@@ -127,7 +127,7 @@ namespace CryptoExchange.Net
var arraysParameters = parameters.Where(p => p.Value.GetType().IsArray).ToList();
foreach (var arrayEntry in arraysParameters)
{
- if(serializationType == ArrayParametersSerialization.Array)
+ if (serializationType == ArrayParametersSerialization.Array)
uriString += $"{string.Join("&", ((object[])(urlEncodeValues ? Uri.EscapeDataString(arrayEntry.Value.ToString()) : arrayEntry.Value)).Select(v => $"{arrayEntry.Key}[]={v}"))}&";
else
{
@@ -369,6 +369,26 @@ namespace CryptoExchange.Net
return url.TrimEnd('/');
}
+
+ ///
+ /// Fill parameters in a path. Parameters are specified by '{}' and should be specified in occuring sequence
+ ///
+ /// The total path string
+ /// The values to fill
+ ///
+ public static string FillPathParameters(this string path, params string[] values)
+ {
+ foreach (var value in values)
+ {
+ var index = path.IndexOf("{}", StringComparison.Ordinal);
+ if (index >= 0)
+ {
+ path = path.Remove(index, 2);
+ path = path.Insert(index, value);
+ }
+ }
+ return path;
+ }
}
}
diff --git a/CryptoExchange.Net/Objects/Options.cs b/CryptoExchange.Net/Objects/Options.cs
index d16e33d..b828208 100644
--- a/CryptoExchange.Net/Objects/Options.cs
+++ b/CryptoExchange.Net/Objects/Options.cs
@@ -60,33 +60,42 @@ namespace CryptoExchange.Net.Objects
public bool ChecksumValidationEnabled { get; set; } = true;
}
- ///
- /// Base client options
- ///
- public class ClientOptions : BaseOptions
+ public class SubClientOptions
{
- private string _baseAddress = string.Empty;
-
///
- /// The base address of the client
+ /// The base address of the sub client
///
- public string BaseAddress
- {
- get => _baseAddress;
- set
- {
- if (value == null)
- return;
-
- _baseAddress = value;
- }
- }
+ public string BaseAddress { get; set; }
///
/// The api credentials used for signing requests
///
public ApiCredentials? ApiCredentials { get; set; }
+ ///
+ /// Copy the values of the def to the input
+ ///
+ ///
+ ///
+ ///
+ public new void Copy(T input, T def) where T : SubClientOptions
+ {
+ input.BaseAddress = def.BaseAddress;
+ input.ApiCredentials = def.ApiCredentials?.Copy();
+ }
+
+ ///
+ public override string ToString()
+ {
+ return $"{base.ToString()}, Credentials: {(ApiCredentials == null ? "-" : "Set")}, BaseAddress: {BaseAddress}";
+ }
+ }
+
+ ///
+ /// Base client options
+ ///
+ public class ClientOptions : BaseOptions
+ {
///
/// Proxy to use when connecting
///
@@ -102,22 +111,17 @@ namespace CryptoExchange.Net.Objects
{
base.Copy(input, def);
- input.BaseAddress = def.BaseAddress;
- input.ApiCredentials = def.ApiCredentials?.Copy();
input.Proxy = def.Proxy;
}
///
public override string ToString()
{
- return $"{base.ToString()}, Credentials: {(ApiCredentials == null ? "-" : "Set")}, BaseAddress: {BaseAddress}, Proxy: {(Proxy == null ? "-" : Proxy.Host)}";
+ return $"{base.ToString()}, Proxy: {(Proxy == null ? "-" : Proxy.Host)}";
}
}
- ///
- /// Base for rest client options
- ///
- public class RestClientOptions : ClientOptions
+ public class RestSubClientOptions: SubClientOptions
{
///
/// List of rate limiters to use
@@ -129,6 +133,32 @@ namespace CryptoExchange.Net.Objects
///
public RateLimitingBehaviour RateLimitingBehaviour { get; set; } = RateLimitingBehaviour.Wait;
+ ///
+ /// Copy the values of the def to the input
+ ///
+ ///
+ ///
+ ///
+ public new void Copy(T input, T def) where T : RestSubClientOptions
+ {
+ base.Copy(input, def);
+
+ input.RateLimiters = def.RateLimiters.ToList();
+ input.RateLimitingBehaviour = def.RateLimitingBehaviour;
+ }
+
+ ///
+ public override string ToString()
+ {
+ return $"{base.ToString()}, RateLimiters: {RateLimiters.Count}, RateLimitBehaviour: {RateLimitingBehaviour}";
+ }
+ }
+
+ ///
+ /// Base for rest client options
+ ///
+ public class RestClientOptions : ClientOptions
+ {
///
/// The time the server has to respond to a request before timing out
///
@@ -150,18 +180,21 @@ namespace CryptoExchange.Net.Objects
base.Copy(input, def);
input.HttpClient = def.HttpClient;
- input.RateLimiters = def.RateLimiters.ToList();
- input.RateLimitingBehaviour = def.RateLimitingBehaviour;
input.RequestTimeout = def.RequestTimeout;
}
///
public override string ToString()
{
- return $"{base.ToString()}, RateLimiters: {RateLimiters.Count}, RateLimitBehaviour: {RateLimitingBehaviour}, RequestTimeout: {RequestTimeout:c}, HttpClient: {(HttpClient == null ? "-": "set")}";
+ return $"{base.ToString()}, RequestTimeout: {RequestTimeout:c}, HttpClient: {(HttpClient == null ? "-": "set")}";
}
}
+ public class SocketSubClientOptions: SubClientOptions
+ {
+ // TODO do we need this?
+ }
+
///
/// Base for socket client options
///
diff --git a/CryptoExchange.Net/RestClient.cs b/CryptoExchange.Net/RestClient.cs
index 62cdc55..4b2d229 100644
--- a/CryptoExchange.Net/RestClient.cs
+++ b/CryptoExchange.Net/RestClient.cs
@@ -59,11 +59,6 @@ namespace CryptoExchange.Net
///
protected string requestBodyEmptyContent = "{}";
- ///
- /// List of rate limiters
- ///
- protected IEnumerable RateLimiters { get; }
-
///
public int TotalRequestsMade { get; private set; }
@@ -82,8 +77,7 @@ namespace CryptoExchange.Net
///
/// The name of the exchange this client is for
/// The options for this client
- /// The authentication provider for this client (can be null if no credentials are provided)
- protected RestClient(string exchangeName, RestClientOptions exchangeOptions, AuthenticationProvider? authenticationProvider) : base(exchangeName, exchangeOptions, authenticationProvider)
+ protected RestClient(string exchangeName, RestClientOptions exchangeOptions) : base(exchangeName, exchangeOptions)
{
if (exchangeOptions == null)
throw new ArgumentNullException(nameof(exchangeOptions));
@@ -91,10 +85,6 @@ namespace CryptoExchange.Net
ClientOptions = exchangeOptions;
RequestFactory.Configure(exchangeOptions.RequestTimeout, exchangeOptions.Proxy, exchangeOptions.HttpClient);
- var rateLimiters = new List();
- foreach (var rateLimiter in exchangeOptions.RateLimiters)
- rateLimiters.Add(rateLimiter);
- RateLimiters = rateLimiters;
}
///
@@ -114,30 +104,32 @@ namespace CryptoExchange.Net
///
[return: NotNull]
protected virtual async Task> SendRequestAsync(
+ RestSubClient subClient,
Uri uri,
HttpMethod method,
- CancellationToken cancellationToken,
+ CancellationToken cancellationToken,
Dictionary? parameters = null,
bool signed = false,
HttpMethodParameterPosition? parameterPosition = null,
ArrayParametersSerialization? arraySerialization = null,
int requestWeight = 1,
JsonSerializer? deserializer = null,
- Dictionary? additionalHeaders = null) where T : class
+ Dictionary? additionalHeaders = null
+ ) where T : class
{
var requestId = NextId();
log.Write(LogLevel.Debug, $"[{requestId}] Creating request for " + uri);
- if (signed && authProvider == null)
+ if (signed && subClient.AuthenticationProvider == null)
{
log.Write(LogLevel.Warning, $"[{requestId}] Request {uri.AbsolutePath} failed because no ApiCredentials were provided");
return new WebCallResult(null, null, null, new NoApiCredentialsError());
}
var paramsPosition = parameterPosition ?? ParameterPositions[method];
- var request = ConstructRequest(uri, method, parameters, signed, paramsPosition, arraySerialization ?? this.arraySerialization, requestId, additionalHeaders);
- foreach (var limiter in RateLimiters)
+ var request = ConstructRequest(subClient, uri, method, parameters, signed, paramsPosition, arraySerialization ?? this.arraySerialization, requestId, additionalHeaders);
+ foreach (var limiter in subClient.RateLimiters)
{
- var limitResult = await limiter.LimitRequestAsync(log, uri.AbsolutePath, method, signed, ClientOptions.ApiCredentials?.Key, ClientOptions.RateLimitingBehaviour, requestWeight, cancellationToken).ConfigureAwait(false);
+ var limitResult = await limiter.LimitRequestAsync(log, uri.AbsolutePath, method, signed, subClient.Options.ApiCredentials?.Key, subClient.Options.RateLimitingBehaviour, requestWeight, cancellationToken).ConfigureAwait(false);
if (!limitResult.Success)
return new WebCallResult(null, null, null, limitResult.Error);
}
@@ -275,6 +267,7 @@ namespace CryptoExchange.Net
/// Additional headers to send with the request
///
protected virtual IRequest ConstructRequest(
+ SubClient subClient,
Uri uri,
HttpMethod method,
Dictionary? parameters,
@@ -287,8 +280,8 @@ namespace CryptoExchange.Net
parameters ??= new Dictionary();
var uriString = uri.ToString();
- if (authProvider != null)
- parameters = authProvider.AddAuthenticationToParameters(uriString, method, parameters, signed, parameterPosition, arraySerialization);
+ if (subClient.AuthenticationProvider != null)
+ parameters = subClient.AuthenticationProvider.AddAuthenticationToParameters(uriString, method, parameters, signed, parameterPosition, arraySerialization);
if (parameterPosition == HttpMethodParameterPosition.InUri && parameters?.Any() == true)
uriString += "?" + parameters.CreateParamString(true, arraySerialization);
@@ -298,8 +291,8 @@ namespace CryptoExchange.Net
request.Accept = Constants.JsonContentHeader;
var headers = new Dictionary();
- if (authProvider != null)
- headers = authProvider.AddAuthenticationToHeaders(uriString, method, parameters!, signed, parameterPosition, arraySerialization);
+ if (subClient.AuthenticationProvider != null)
+ headers = subClient.AuthenticationProvider.AddAuthenticationToHeaders(uriString, method, parameters!, signed, parameterPosition, arraySerialization);
foreach (var header in headers)
request.AddHeader(header.Key, header.Value);
diff --git a/CryptoExchange.Net/RestSubClient.cs b/CryptoExchange.Net/RestSubClient.cs
new file mode 100644
index 0000000..f77a7d5
--- /dev/null
+++ b/CryptoExchange.Net/RestSubClient.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using System.Linq;
+using System.Net.Http;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Web;
+using CryptoExchange.Net.Authentication;
+using CryptoExchange.Net.Interfaces;
+using CryptoExchange.Net.Objects;
+using CryptoExchange.Net.Requests;
+using Microsoft.Extensions.Logging;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace CryptoExchange.Net
+{
+ ///
+ /// Base rest client
+ ///
+ public abstract class RestSubClient: SubClient
+ {
+ internal RestSubClientOptions Options { get; }
+
+ ///
+ /// List of rate limiters
+ ///
+ internal IEnumerable RateLimiters { get; }
+
+ public RestSubClient(RestSubClientOptions options, AuthenticationProvider? authProvider): base(options,authProvider)
+ {
+ Options = options;
+
+ var rateLimiters = new List();
+ foreach (var rateLimiter in options.RateLimiters)
+ rateLimiters.Add(rateLimiter);
+ RateLimiters = rateLimiters;
+ }
+
+ }
+}
diff --git a/CryptoExchange.Net/SocketClient.cs b/CryptoExchange.Net/SocketClient.cs
index fc4c29f..3073a6f 100644
--- a/CryptoExchange.Net/SocketClient.cs
+++ b/CryptoExchange.Net/SocketClient.cs
@@ -104,8 +104,7 @@ namespace CryptoExchange.Net
///
/// The name of the exchange this client is for
/// The options for this client
- /// The authentication provider for this client (can be null if no credentials are provided)
- protected SocketClient(string exchangeName, SocketClientOptions exchangeOptions, AuthenticationProvider? authenticationProvider): base(exchangeName, exchangeOptions, authenticationProvider)
+ protected SocketClient(string exchangeName, SocketClientOptions exchangeOptions): base(exchangeName, exchangeOptions)
{
if (exchangeOptions == null)
throw new ArgumentNullException(nameof(exchangeOptions));
@@ -134,9 +133,9 @@ namespace CryptoExchange.Net
/// The handler of update data
/// Cancellation token for closing this subscription
///
- protected virtual Task> SubscribeAsync(object? request, string? identifier, bool authenticated, Action> dataHandler, CancellationToken ct)
+ protected virtual Task> SubscribeAsync(SocketSubClient subClient, object? request, string? identifier, bool authenticated, Action> dataHandler, CancellationToken ct)
{
- return SubscribeAsync(ClientOptions.BaseAddress, request, identifier, authenticated, dataHandler, ct);
+ return SubscribeAsync(subClient, subClient.Options.BaseAddress, request, identifier, authenticated, dataHandler, ct);
}
///
@@ -150,7 +149,7 @@ namespace CryptoExchange.Net
/// The handler of update data
/// Cancellation token for closing this subscription
///
- protected virtual async Task> SubscribeAsync(string url, object? request, string? identifier, bool authenticated, Action> dataHandler, CancellationToken ct)
+ protected virtual async Task> SubscribeAsync(SocketSubClient subClient, string url, object? request, string? identifier, bool authenticated, Action> dataHandler, CancellationToken ct)
{
SocketConnection socketConnection;
SocketSubscription subscription;
@@ -169,7 +168,7 @@ namespace CryptoExchange.Net
try
{
// Get a new or existing socket connection
- socketConnection = GetSocketConnection(url, authenticated);
+ socketConnection = GetSocketConnection(subClient, url, authenticated);
// Add a subscription on the socket connection
subscription = AddSubscription(request, identifier, true, socketConnection, dataHandler);
@@ -254,9 +253,9 @@ namespace CryptoExchange.Net
/// The request to send, will be serialized to json
/// If the query is to an authenticated endpoint
///
- protected virtual Task> QueryAsync(object request, bool authenticated)
+ protected virtual Task> QueryAsync(SocketSubClient subClient, object request, bool authenticated)
{
- return QueryAsync(ClientOptions.BaseAddress, request, authenticated);
+ return QueryAsync(subClient, subClient.Options.BaseAddress, request, authenticated);
}
///
@@ -267,14 +266,14 @@ namespace CryptoExchange.Net
/// The request to send
/// Whether the socket should be authenticated
///
- protected virtual async Task> QueryAsync(string url, object request, bool authenticated)
+ protected virtual async Task> QueryAsync(SocketSubClient subClient, string url, object request, bool authenticated)
{
SocketConnection socketConnection;
var released = false;
await semaphoreSlim.WaitAsync().ConfigureAwait(false);
try
{
- socketConnection = GetSocketConnection(url, authenticated);
+ socketConnection = GetSocketConnection(subClient, url, authenticated);
if (ClientOptions.SocketSubscriptionsCombineTarget == 1)
{
// Can release early when only a single sub per connection
@@ -481,9 +480,10 @@ namespace CryptoExchange.Net
/// The address the socket is for
/// Whether the socket should be authenticated
///
- protected virtual SocketConnection GetSocketConnection(string address, bool authenticated)
+ protected virtual SocketConnection GetSocketConnection(SocketSubClient subClient, string address, bool authenticated)
{
- var socketResult = sockets.Where(s => s.Value.Socket.Url.TrimEnd('/') == address.TrimEnd('/')
+ var socketResult = sockets.Where(s => s.Value.Socket.Url.TrimEnd('/') == address.TrimEnd('/')
+ && (s.Value.SubClient.GetType() == subClient.GetType())
&& (s.Value.Authenticated == authenticated || !authenticated) && s.Value.Connected).OrderBy(s => s.Value.SubscriptionCount).FirstOrDefault();
var result = socketResult.Equals(default(KeyValuePair)) ? null : socketResult.Value;
if (result != null)
@@ -497,7 +497,7 @@ namespace CryptoExchange.Net
// Create new socket
var socket = CreateSocket(address);
- var socketConnection = new SocketConnection(this, socket);
+ var socketConnection = new SocketConnection(this, subClient, socket);
socketConnection.UnhandledMessage += HandleUnhandledMessage;
foreach (var kvp in genericHandlers)
{
diff --git a/CryptoExchange.Net/SocketSubClient.cs b/CryptoExchange.Net/SocketSubClient.cs
new file mode 100644
index 0000000..e90a814
--- /dev/null
+++ b/CryptoExchange.Net/SocketSubClient.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using System.Linq;
+using System.Net.Http;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Web;
+using CryptoExchange.Net.Authentication;
+using CryptoExchange.Net.Interfaces;
+using CryptoExchange.Net.Objects;
+using CryptoExchange.Net.Requests;
+using Microsoft.Extensions.Logging;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace CryptoExchange.Net
+{
+ ///
+ /// Base rest client
+ ///
+ public abstract class SocketSubClient : SubClient
+ {
+ internal SocketSubClientOptions Options { get; }
+
+ public SocketSubClient(SocketSubClientOptions options, AuthenticationProvider? authProvider): base(options,authProvider)
+ {
+ Options = options;
+ }
+
+ }
+}
diff --git a/CryptoExchange.Net/Sockets/CryptoExchangeWebSocketClient.cs b/CryptoExchange.Net/Sockets/CryptoExchangeWebSocketClient.cs
index e0166ff..188d60e 100644
--- a/CryptoExchange.Net/Sockets/CryptoExchangeWebSocketClient.cs
+++ b/CryptoExchange.Net/Sockets/CryptoExchangeWebSocketClient.cs
@@ -224,7 +224,7 @@ namespace CryptoExchange.Net.Sockets
try
{
using CancellationTokenSource tcs = new CancellationTokenSource(TimeSpan.FromSeconds(10));
- await _socket.ConnectAsync(new Uri(Url), default).ConfigureAwait(false);
+ await _socket.ConnectAsync(new Uri(Url), tcs.Token).ConfigureAwait(false);
Handle(openHandlers);
}
diff --git a/CryptoExchange.Net/Sockets/SocketConnection.cs b/CryptoExchange.Net/Sockets/SocketConnection.cs
index 2332554..530f7e1 100644
--- a/CryptoExchange.Net/Sockets/SocketConnection.cs
+++ b/CryptoExchange.Net/Sockets/SocketConnection.cs
@@ -77,6 +77,8 @@ namespace CryptoExchange.Net.Sockets
///
public IWebsocket Socket { get; set; }
+ public SocketSubClient SubClient { get; set; }
+
///
/// If the socket should be reconnected upon closing
///
@@ -97,6 +99,11 @@ namespace CryptoExchange.Net.Sockets
///
public DateTime? DisconnectTime { get; set; }
+ ///
+ /// Tag for identificaion
+ ///
+ public string? Tag { get; set; }
+
///
/// If activity is paused
///
@@ -130,10 +137,11 @@ namespace CryptoExchange.Net.Sockets
///
/// The socket client
/// The socket
- public SocketConnection(SocketClient client, IWebsocket socket)
+ public SocketConnection(SocketClient client, SocketSubClient subClient, IWebsocket socket)
{
log = client.log;
socketClient = client;
+ SubClient = subClient;
pendingRequests = new List();
diff --git a/CryptoExchange.Net/SubClient.cs b/CryptoExchange.Net/SubClient.cs
new file mode 100644
index 0000000..6d97a09
--- /dev/null
+++ b/CryptoExchange.Net/SubClient.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using System.Linq;
+using System.Net.Http;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Web;
+using CryptoExchange.Net.Authentication;
+using CryptoExchange.Net.Interfaces;
+using CryptoExchange.Net.Objects;
+using CryptoExchange.Net.Requests;
+using Microsoft.Extensions.Logging;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace CryptoExchange.Net
+{
+ ///
+ /// Base rest client
+ ///
+ public abstract class SubClient
+ {
+ public AuthenticationProvider? AuthenticationProvider { get; }
+ protected string BaseAddress { get; }
+
+ public SubClient(SubClientOptions options, AuthenticationProvider? authProvider)
+ {
+ AuthenticationProvider = authProvider;
+ BaseAddress = options.BaseAddress;
+ }
+
+ }
+}