diff --git a/CryptoExchange.Net/Clients/BaseApiClient.cs b/CryptoExchange.Net/Clients/BaseApiClient.cs
index a4f64c0..c74196a 100644
--- a/CryptoExchange.Net/Clients/BaseApiClient.cs
+++ b/CryptoExchange.Net/Clients/BaseApiClient.cs
@@ -1,7 +1,9 @@
using System;
+using System.Collections.Generic;
using CryptoExchange.Net.Authentication;
using CryptoExchange.Net.Interfaces;
using CryptoExchange.Net.Objects;
+using CryptoExchange.Net.Objects.Errors;
using CryptoExchange.Net.Objects.Options;
using CryptoExchange.Net.SharedApis;
using Microsoft.Extensions.Logging;
@@ -54,6 +56,11 @@ namespace CryptoExchange.Net.Clients
///
public ExchangeOptions ClientOptions { get; }
+ ///
+ /// Mapping of a response code to known error types
+ ///
+ protected internal virtual ErrorCollection ErrorMapping { get; } = new ErrorCollection([]);
+
///
/// ctor
///
@@ -87,6 +94,16 @@ namespace CryptoExchange.Net.Clients
///
public abstract string FormatSymbol(string baseAsset, string quoteAsset, TradingMode tradingMode, DateTime? deliverDate = null);
+ ///
+ /// Get error info for a response code
+ ///
+ public ErrorInfo GetErrorInfo(int code, string? message = null) => GetErrorInfo(code.ToString(), message);
+
+ ///
+ /// Get error info for a response code
+ ///
+ public ErrorInfo GetErrorInfo(string code, string? message = null) => ErrorMapping.GetErrorInfo(code.ToString(), message);
+
///
public void SetApiCredentials(T credentials) where T : ApiCredentials
{
diff --git a/CryptoExchange.Net/Clients/RestApiClient.cs b/CryptoExchange.Net/Clients/RestApiClient.cs
index 3d3532e..3f54afe 100644
--- a/CryptoExchange.Net/Clients/RestApiClient.cs
+++ b/CryptoExchange.Net/Clients/RestApiClient.cs
@@ -11,6 +11,7 @@ using CryptoExchange.Net.Caching;
using CryptoExchange.Net.Interfaces;
using CryptoExchange.Net.Logging.Extensions;
using CryptoExchange.Net.Objects;
+using CryptoExchange.Net.Objects.Errors;
using CryptoExchange.Net.Objects.Options;
using CryptoExchange.Net.RateLimiting;
using CryptoExchange.Net.RateLimiting.Interfaces;
@@ -485,8 +486,10 @@ namespace CryptoExchange.Net.Clients
error = ParseErrorResponse((int)response.StatusCode, response.ResponseHeaders, accessor, readResult.Error?.Exception);
}
+#pragma warning disable CS0618 // Type or member is obsolete
if (error.Code == null || error.Code == 0)
error.Code = (int)response.StatusCode;
+#pragma warning restore CS0618 // Type or member is obsolete
return new WebCallResult(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? accessor.GetOriginalString() : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, error!);
}
@@ -499,8 +502,7 @@ namespace CryptoExchange.Net.Clients
if (!valid)
{
// Invalid json
- var error = new DeserializeError("Failed to parse response: " + valid.Error!.Message, valid.Error.Exception);
- return new WebCallResult(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? accessor.GetOriginalString() : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, error);
+ return new WebCallResult(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? accessor.GetOriginalString() : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, valid.Error);
}
// Json response received
@@ -526,7 +528,8 @@ namespace CryptoExchange.Net.Clients
catch (HttpRequestException requestException)
{
// Request exception, can't reach server for instance
- return new WebCallResult(null, null, sw.Elapsed, null, null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, new WebError(requestException.Message, exception: requestException));
+ var error = new WebError(requestException.Message, requestException);
+ return new WebCallResult(null, null, sw.Elapsed, null, null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, error);
}
catch (OperationCanceledException canceledException)
{
@@ -633,7 +636,7 @@ namespace CryptoExchange.Net.Clients
///
protected virtual Error ParseErrorResponse(int httpStatusCode, KeyValuePair[] responseHeaders, IMessageAccessor accessor, Exception? exception)
{
- return new ServerError(null, "Unknown request error", exception);
+ return new ServerError(ErrorInfo.Unknown, exception);
}
///
diff --git a/CryptoExchange.Net/Clients/SocketApiClient.cs b/CryptoExchange.Net/Clients/SocketApiClient.cs
index fcc0565..faac4b1 100644
--- a/CryptoExchange.Net/Clients/SocketApiClient.cs
+++ b/CryptoExchange.Net/Clients/SocketApiClient.cs
@@ -1,6 +1,7 @@
using CryptoExchange.Net.Interfaces;
using CryptoExchange.Net.Logging.Extensions;
using CryptoExchange.Net.Objects;
+using CryptoExchange.Net.Objects.Errors;
using CryptoExchange.Net.Objects.Options;
using CryptoExchange.Net.Objects.Sockets;
using CryptoExchange.Net.RateLimiting;
@@ -265,7 +266,7 @@ namespace CryptoExchange.Net.Clients
if (socketConnection.PausedActivity)
{
_logger.HasBeenPausedCantSubscribeAtThisMoment(socketConnection.SocketId);
- return new CallResult(new ServerError("Socket is paused"));
+ return new CallResult(new ServerError(new ErrorInfo(ErrorType.WebsocketPaused, "Socket is paused")));
}
var waitEvent = new AsyncResetEvent(false);
@@ -368,7 +369,7 @@ namespace CryptoExchange.Net.Clients
if (socketConnection.PausedActivity)
{
_logger.HasBeenPausedCantSendQueryAtThisMoment(socketConnection.SocketId);
- return new CallResult(new ServerError("Socket is paused"));
+ return new CallResult(new ServerError(new ErrorInfo(ErrorType.WebsocketPaused, "Socket is paused")));
}
if (ct.IsCancellationRequested)
diff --git a/CryptoExchange.Net/Converters/SystemTextJson/SystemTextJsonMessageAccessor.cs b/CryptoExchange.Net/Converters/SystemTextJson/SystemTextJsonMessageAccessor.cs
index 0f9645c..2deac5e 100644
--- a/CryptoExchange.Net/Converters/SystemTextJson/SystemTextJsonMessageAccessor.cs
+++ b/CryptoExchange.Net/Converters/SystemTextJson/SystemTextJsonMessageAccessor.cs
@@ -286,7 +286,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
{
// Not a json message
IsValid = false;
- return new CallResult(new DeserializeError("JsonError: " + ex.Message, ex));
+ return new CallResult(new DeserializeError(ex.Message, ex));
}
}
@@ -338,7 +338,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
{
// Value doesn't start with `{` or `[`, prevent deserialization attempt as it's slow
IsValid = false;
- return new CallResult(new ServerError("Not a json value"));
+ return new CallResult(new DeserializeError("Not a json value"));
}
_document = JsonDocument.Parse(data);
diff --git a/CryptoExchange.Net/Objects/Error.cs b/CryptoExchange.Net/Objects/Error.cs
index 61a012b..8c75fd7 100644
--- a/CryptoExchange.Net/Objects/Error.cs
+++ b/CryptoExchange.Net/Objects/Error.cs
@@ -1,4 +1,5 @@
-using System;
+using CryptoExchange.Net.Objects.Errors;
+using System;
namespace CryptoExchange.Net.Objects
{
@@ -7,15 +8,50 @@ namespace CryptoExchange.Net.Objects
///
public abstract class Error
{
+
+ private int? _code;
///
/// The error code from the server
///
- public int? Code { get; set; }
+ [Obsolete("Use ErrorCode instead", false)]
+ public int? Code
+ {
+ get
+ {
+ if (_code.HasValue)
+ return _code;
+
+ return int.TryParse(ErrorCode, out var r) ? r : null;
+ }
+ set
+ {
+ _code = value;
+ }
+ }
///
- /// The message for the error that occurred
+ /// The error code returned by the server
///
- public string Message { get; set; }
+ public string? ErrorCode { get; set; }
+ ///
+ /// The error description
+ ///
+ public string? ErrorDescription { get; set; }
+
+ ///
+ /// Error type
+ ///
+ public ErrorType ErrorType { get; set; }
+
+ ///
+ /// Whether the error is transient and can be retried
+ ///
+ public bool IsTransient { get; set; }
+
+ ///
+ /// The server message for the error that occurred
+ ///
+ public string? Message { get; set; }
///
/// Underlying exception
@@ -25,10 +61,13 @@ namespace CryptoExchange.Net.Objects
///
/// ctor
///
- protected Error (int? code, string message, Exception? exception)
+ protected Error(string? errorCode, ErrorInfo errorInfo, Exception? exception)
{
- Code = code;
- Message = message;
+ ErrorCode = errorCode;
+ ErrorType = errorInfo.ErrorType;
+ Message = errorInfo.Message;
+ ErrorDescription = errorInfo.ErrorDescription;
+ IsTransient = errorInfo.IsTransient;
Exception = exception;
}
@@ -38,7 +77,7 @@ namespace CryptoExchange.Net.Objects
///
public override string ToString()
{
- return Code != null ? $"[{GetType().Name}] {Code}: {Message}" : $"[{GetType().Name}] {Message}";
+ return ErrorCode != null ? $"[{GetType().Name}.{ErrorType}] {ErrorCode}: {Message ?? ErrorDescription}" : $"[{GetType().Name}.{ErrorType}] {Message ?? ErrorDescription}";
}
}
@@ -48,19 +87,24 @@ namespace CryptoExchange.Net.Objects
public class CantConnectError : Error
{
///
- /// ctor
+ /// Default error info
///
- public CantConnectError() : base(null, "Can't connect to the server", null) { }
+ protected static readonly ErrorInfo _errorInfo = new ErrorInfo(ErrorType.UnableToConnect, false, "Can't connect to the server");
///
/// ctor
///
- public CantConnectError(Exception? exception) : base(null, "Can't connect to the server", exception) { }
+ public CantConnectError() : base(null, _errorInfo, null) { }
///
/// ctor
///
- protected CantConnectError(int? code, string message, Exception? exception) : base(code, message, exception) { }
+ public CantConnectError(Exception? exception) : base(null, _errorInfo, exception) { }
+
+ ///
+ /// ctor
+ ///
+ protected CantConnectError(ErrorInfo info, Exception? exception) : base(null, info, exception) { }
}
///
@@ -69,14 +113,19 @@ namespace CryptoExchange.Net.Objects
public class NoApiCredentialsError : Error
{
///
- /// ctor
+ /// Default error info
///
- public NoApiCredentialsError() : base(null, "No credentials provided for private endpoint", null) { }
+ protected static readonly ErrorInfo _errorInfo = new ErrorInfo(ErrorType.MissingCredentials, false, "No credentials provided for private endpoint");
///
/// ctor
///
- protected NoApiCredentialsError(int? code, string message, Exception? exception) : base(code, message, exception) { }
+ public NoApiCredentialsError() : base(null, _errorInfo, null) { }
+
+ ///
+ /// ctor
+ ///
+ protected NoApiCredentialsError(ErrorInfo info, Exception? exception) : base(null, info, exception) { }
}
///
@@ -87,12 +136,19 @@ namespace CryptoExchange.Net.Objects
///
/// ctor
///
- public ServerError(string message) : base(null, message, null) { }
+ public ServerError(ErrorInfo errorInfo, Exception? exception = null)
+ : base(null, errorInfo, exception) { }
///
/// ctor
///
- public ServerError(int? code, string message, Exception? exception = null) : base(code, message, exception) { }
+ public ServerError(int errorCode, ErrorInfo errorInfo, Exception? exception = null)
+ : this(errorCode.ToString(), errorInfo, exception) { }
+
+ ///
+ /// ctor
+ ///
+ public ServerError(string errorCode, ErrorInfo errorInfo, Exception? exception = null) : base(errorCode, errorInfo, exception) { }
}
///
@@ -101,14 +157,30 @@ namespace CryptoExchange.Net.Objects
public class WebError : Error
{
///
- /// ctor
+ /// Default error info
///
- public WebError(string message, Exception? exception = null) : base(null, message, exception) { }
+ protected static readonly ErrorInfo _errorInfo = new ErrorInfo(ErrorType.NetworkError, false, "Failed to complete the request to the server due to a network error");
///
/// ctor
///
- public WebError(int code, string message, Exception? exception = null) : base(code, message, exception) { }
+ public WebError(string? message = null, Exception? exception = null) : base(null, _errorInfo with { Message = (message?.Length > 0 ? _errorInfo.Message + ": " + message : _errorInfo.Message) }, exception) { }
+ }
+
+ ///
+ /// Timeout error waiting for a response from the server
+ ///
+ public class TimeoutError : Error
+ {
+ ///
+ /// Default error info
+ ///
+ protected static readonly ErrorInfo _errorInfo = new ErrorInfo(ErrorType.Timeout, false, "Failed to receive a response from the server in time");
+
+ ///
+ /// ctor
+ ///
+ public TimeoutError(string? message = null, Exception? exception = null) : base(null, _errorInfo with { Message = (message?.Length > 0 ? _errorInfo.Message + ": " + message : _errorInfo.Message) }, exception) { }
}
///
@@ -117,30 +189,14 @@ namespace CryptoExchange.Net.Objects
public class DeserializeError : Error
{
///
- /// ctor
+ /// Default error info
///
- public DeserializeError(string message, Exception? exception = null) : base(null, message, exception) { }
+ protected static readonly ErrorInfo _errorInfo = new ErrorInfo(ErrorType.DeserializationFailed, false, "Failed to deserialize data");
///
/// ctor
///
- protected DeserializeError(int? code, string message, Exception? exception = null) : base(code, message, exception) { }
- }
-
- ///
- /// Unknown error
- ///
- public class UnknownError : Error
- {
- ///
- /// ctor
- ///
- public UnknownError(string message, Exception? exception = null) : base(null, message, exception) { }
-
- ///
- /// ctor
- ///
- protected UnknownError(int? code, string message, Exception? exception = null): base(code, message, exception) { }
+ public DeserializeError(string? message = null, Exception? exception = null) : base(null, _errorInfo with { Message = (message?.Length > 0 ? _errorInfo.Message + ": " + message : _errorInfo.Message) }, exception) { }
}
///
@@ -149,14 +205,28 @@ namespace CryptoExchange.Net.Objects
public class ArgumentError : Error
{
///
- /// ctor
+ /// Default error info for missing parameter
///
- public ArgumentError(string message) : base(null, "Invalid parameter: " + message, null) { }
+ protected static readonly ErrorInfo _missingInfo = new ErrorInfo(ErrorType.MissingParameter, false, "Missing parameter");
+ ///
+ /// Default error info for invalid parameter
+ ///
+ protected static readonly ErrorInfo _invalidInfo = new ErrorInfo(ErrorType.InvalidParameter, false, "Invalid parameter");
///
/// ctor
///
- protected ArgumentError(int? code, string message, Exception? exception = null) : base(code, message, exception) { }
+ public static ArgumentError Missing(string parameterName) => new ArgumentError(_missingInfo with { Message = $"{_missingInfo.Message} '{parameterName}'" }, null);
+
+ ///
+ /// ctor
+ ///
+ public static ArgumentError Invalid(string parameterName, string message) => new ArgumentError(_invalidInfo with { Message = $"{_invalidInfo.Message} '{parameterName}': {message}" }, null);
+
+ ///
+ /// ctor
+ ///
+ protected ArgumentError(ErrorInfo info, Exception? exception) : base(null, info, exception) { }
}
///
@@ -172,7 +242,7 @@ namespace CryptoExchange.Net.Objects
///
/// ctor
///
- protected BaseRateLimitError(int? code, string message, Exception? exception) : base(code, message, exception) { }
+ protected BaseRateLimitError(ErrorInfo errorInfo, Exception? exception) : base(null, errorInfo, exception) { }
}
///
@@ -181,15 +251,19 @@ namespace CryptoExchange.Net.Objects
public class ClientRateLimitError : BaseRateLimitError
{
///
- /// ctor
+ /// Default error info
///
- ///
- public ClientRateLimitError(string message) : base(null, "Client rate limit exceeded: " + message, null) { }
+ protected static readonly ErrorInfo _errorInfo = new ErrorInfo(ErrorType.RequestRateLimited, false, "Client rate limit exceeded");
///
/// ctor
///
- protected ClientRateLimitError(int? code, string message, Exception? exception = null) : base(code, message, exception) { }
+ public ClientRateLimitError(string? message = null, Exception? exception = null) : base(_errorInfo with { Message = (message?.Length > 0 ? _errorInfo.Message + ": " + message : _errorInfo.Message) }, exception) { }
+
+ ///
+ /// ctor
+ ///
+ protected ClientRateLimitError(ErrorInfo info, Exception? exception) : base(info, exception) { }
}
///
@@ -198,14 +272,19 @@ namespace CryptoExchange.Net.Objects
public class ServerRateLimitError : BaseRateLimitError
{
///
- /// ctor
+ /// Default error info
///
- public ServerRateLimitError(string? message = null, Exception? exception = null) : base(null, "Server rate limit exceeded" + (message?.Length > 0 ? " : " + message : null), exception) { }
+ protected static readonly ErrorInfo _errorInfo = new ErrorInfo(ErrorType.RequestRateLimited, false, "Server rate limit exceeded");
///
/// ctor
///
- protected ServerRateLimitError(int? code, string message, Exception? exception = null) : base(code, message, exception) { }
+ public ServerRateLimitError(string? message = null, Exception? exception = null) : base(_errorInfo with { Message = (message?.Length > 0 ? _errorInfo.Message + ": " + message : _errorInfo.Message) }, exception) { }
+
+ ///
+ /// ctor
+ ///
+ protected ServerRateLimitError(ErrorInfo info, Exception? exception) : base(info, exception) { }
}
///
@@ -214,14 +293,19 @@ namespace CryptoExchange.Net.Objects
public class CancellationRequestedError : Error
{
///
- /// ctor
+ /// Default error info
///
- public CancellationRequestedError(Exception? exception = null) : base(null, "Cancellation requested", exception) { }
+ protected static readonly ErrorInfo _errorInfo = new ErrorInfo(ErrorType.MissingCredentials, false, "Cancellation requested");
///
/// ctor
///
- public CancellationRequestedError(int? code, string message, Exception? exception = null) : base(code, message, exception) { }
+ public CancellationRequestedError(Exception? exception = null) : base(null, _errorInfo, null) { }
+
+ ///
+ /// ctor
+ ///
+ protected CancellationRequestedError(ErrorInfo info, Exception? exception) : base(null, info, exception) { }
}
///
@@ -230,13 +314,18 @@ namespace CryptoExchange.Net.Objects
public class InvalidOperationError : Error
{
///
- /// ctor
+ /// Default error info
///
- public InvalidOperationError(string message, Exception? exception = null) : base(null, message, exception) { }
+ protected static readonly ErrorInfo _errorInfo = new ErrorInfo(ErrorType.InvalidOperation, false, "");
///
/// ctor
///
- protected InvalidOperationError(int? code, string message, Exception? exception = null) : base(code, message, exception) { }
+ public InvalidOperationError(string message) : base(null, _errorInfo with { Message = message }, null) { }
+
+ ///
+ /// ctor
+ ///
+ protected InvalidOperationError(ErrorInfo info, Exception? exception) : base(null, info, exception) { }
}
}
diff --git a/CryptoExchange.Net/Objects/Errors/ErrorCollection.cs b/CryptoExchange.Net/Objects/Errors/ErrorCollection.cs
new file mode 100644
index 0000000..ab91b30
--- /dev/null
+++ b/CryptoExchange.Net/Objects/Errors/ErrorCollection.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace CryptoExchange.Net.Objects.Errors
+{
+
+
+ ///
+ /// Error mapping collection
+ ///
+ public class ErrorCollection
+ {
+ private Dictionary _evaluators = new Dictionary();
+ private Dictionary _directMapping = new Dictionary();
+
+ ///
+ /// ctor
+ ///
+ public ErrorCollection(ErrorInfo[] errorMappings, ErrorEvaluator[]? errorTypeEvaluators = null)
+ {
+ foreach (var item in errorMappings)
+ {
+ if (!item.ErrorCodes.Any())
+ throw new Exception("Error codes can't be null in error mapping");
+
+ foreach(var code in item.ErrorCodes!)
+ _directMapping.Add(code, item);
+ }
+
+ if (errorTypeEvaluators == null)
+ return;
+
+ foreach (var item in errorTypeEvaluators)
+ {
+ foreach(var code in item.ErrorCodes)
+ _evaluators.Add(code, item);
+ }
+ }
+
+ ///
+ /// Get error info for an error code
+ ///
+ public ErrorInfo GetErrorInfo(string code, string? message)
+ {
+ message = message ?? "-";
+ if (_directMapping.TryGetValue(code, out var info))
+ return info with { Message = message };
+
+ if (_evaluators.TryGetValue(code, out var eva))
+ return eva.ErrorTypeEvaluator.Invoke(code, message) with { Message = message };
+
+ return ErrorInfo.Unknown with { Message = message };
+ }
+ }
+}
diff --git a/CryptoExchange.Net/Objects/Errors/ErrorEvaluator.cs b/CryptoExchange.Net/Objects/Errors/ErrorEvaluator.cs
new file mode 100644
index 0000000..b245115
--- /dev/null
+++ b/CryptoExchange.Net/Objects/Errors/ErrorEvaluator.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace CryptoExchange.Net.Objects.Errors
+{
+ ///
+ /// Error evaluator
+ ///
+ public class ErrorEvaluator
+ {
+ ///
+ /// Error code
+ ///
+ public string[] ErrorCodes { get; set; }
+
+ ///
+ /// Evaluation callback for determining the error type
+ ///
+ public Func ErrorTypeEvaluator { get; set; }
+
+ ///
+ /// ctor
+ ///
+ public ErrorEvaluator(string errorCode, Func errorTypeEvaluator)
+ {
+ ErrorCodes = [errorCode];
+ ErrorTypeEvaluator = errorTypeEvaluator;
+ }
+
+ ///
+ /// ctor
+ ///
+ public ErrorEvaluator(string[] errorCodes, Func errorTypeEvaluator)
+ {
+ ErrorCodes = errorCodes;
+ ErrorTypeEvaluator = errorTypeEvaluator;
+ }
+ }
+}
diff --git a/CryptoExchange.Net/Objects/Errors/ErrorInfo.cs b/CryptoExchange.Net/Objects/Errors/ErrorInfo.cs
new file mode 100644
index 0000000..0dd38c6
--- /dev/null
+++ b/CryptoExchange.Net/Objects/Errors/ErrorInfo.cs
@@ -0,0 +1,58 @@
+using System;
+
+namespace CryptoExchange.Net.Objects.Errors
+{
+ ///
+ /// Error info
+ ///
+ public record ErrorInfo
+ {
+ ///
+ /// Unknown error info
+ ///
+ public static ErrorInfo Unknown { get; } = new ErrorInfo(ErrorType.Unknown, false, "Unknown error", []);
+
+ ///
+ /// The server error code
+ ///
+ public string[] ErrorCodes { get; set; }
+ ///
+ /// Error description
+ ///
+ public string? ErrorDescription { get; set; }
+ ///
+ /// The error type
+ ///
+ public ErrorType ErrorType { get; set; }
+ ///
+ /// Whether the error is transient and can be retried
+ ///
+ public bool IsTransient { get; set; }
+ ///
+ /// Server response message
+ ///
+ public string? Message { get; set; }
+
+ ///
+ /// ctor
+ ///
+ public ErrorInfo(ErrorType errorType, string description)
+ {
+ ErrorCodes = [];
+ ErrorType = errorType;
+ IsTransient = false;
+ ErrorDescription = description;
+ }
+
+ ///
+ /// ctor
+ ///
+ public ErrorInfo(ErrorType errorType, bool isTransient, string description, params string[] errorCodes)
+ {
+ ErrorCodes = errorCodes;
+ ErrorType = errorType;
+ IsTransient = isTransient;
+ ErrorDescription = description;
+ }
+ }
+}
diff --git a/CryptoExchange.Net/Objects/Errors/ErrorType.cs b/CryptoExchange.Net/Objects/Errors/ErrorType.cs
new file mode 100644
index 0000000..8be8aad
--- /dev/null
+++ b/CryptoExchange.Net/Objects/Errors/ErrorType.cs
@@ -0,0 +1,166 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace CryptoExchange.Net.Objects.Errors
+{
+ ///
+ /// Type of error
+ ///
+ public enum ErrorType
+ {
+ #region Library errors
+
+ ///
+ /// Failed to connect to server
+ ///
+ UnableToConnect,
+ ///
+ /// Failed to complete the request to the server
+ ///
+ NetworkError,
+ ///
+ /// No API credentials have been specified
+ ///
+ MissingCredentials,
+ ///
+ /// Invalid parameter value
+ ///
+ InvalidParameter,
+ ///
+ /// Invalid listen key
+ ///
+ InvalidListenKey,
+ ///
+ /// Missing parameter value
+ ///
+ MissingParameter,
+ ///
+ /// Cancellation requested by user
+ ///
+ CancellationRequested,
+ ///
+ /// Invalid operation requested
+ ///
+ InvalidOperation,
+ ///
+ /// Failed to deserialize data
+ ///
+ DeserializationFailed,
+ ///
+ /// Websocket is temporarily paused
+ ///
+ WebsocketPaused,
+ ///
+ /// Timeout while waiting for data from the order book subscription
+ ///
+ OrderBookTimeout,
+ ///
+ /// All orders failed for a multi-order operation
+ ///
+ AllOrdersFailed,
+ ///
+ /// Request timeout
+ ///
+ Timeout,
+
+ #endregion
+
+ #region Server errors
+
+ ///
+ /// Unknown error
+ ///
+ Unknown,
+ ///
+ /// Not authorized or insufficient permissions
+ ///
+ Unauthorized,
+ ///
+ /// Request rate limit error, too many requests
+ ///
+ RequestRateLimited,
+ ///
+ /// Connection rate limit error, too many connections
+ ///
+ ConnectionRateLimited,
+ ///
+ /// Subscription rate limit error, too many subscriptions
+ ///
+ SubscriptionRateLimited,
+ ///
+ /// Order rate limit error, too many orders
+ ///
+ OrderRateLimited,
+ ///
+ /// Timestamp invalid
+ ///
+ TimestampInvalid,
+ ///
+ /// Request signature invalid
+ ///
+ SignatureInvalid,
+ ///
+ /// Unknown symbol
+ ///
+ UnknownSymbol,
+ ///
+ /// Unknown asset
+ ///
+ UnknownAsset,
+ ///
+ /// Unknown order
+ ///
+ UnknownOrder,
+ ///
+ /// Duplicate subscription
+ ///
+ DuplicateSubscription,
+ ///
+ /// Invalid quantity
+ ///
+ QuantityInvalid,
+ ///
+ /// Invalid price
+ ///
+ PriceInvalid,
+ ///
+ /// Parameter(s) for stop or tp/sl order invalid
+ ///
+ StopParametersInvalid,
+ ///
+ /// Not enough balance to execute order
+ ///
+ BalanceInsufficient,
+ ///
+ /// Client order id already in use
+ ///
+ DuplicateClientOrderId,
+ ///
+ /// Symbol is not currently trading
+ ///
+ SymbolNotTrading,
+ ///
+ /// Order rejected due to order type or time in force restrictions
+ ///
+ OrderConfigurationRejected,
+ ///
+ /// Order type not allowed
+ ///
+ OrderTypeInvalid,
+ ///
+ /// There is no open position
+ ///
+ NoPosition,
+ ///
+ /// Error in the internal system
+ ///
+ SystemError,
+ ///
+ /// The target object is not in the correct state for an operation
+ ///
+ TargetIncorrectState
+
+ #endregion
+ }
+}
diff --git a/CryptoExchange.Net/OrderBook/SymbolOrderBook.cs b/CryptoExchange.Net/OrderBook/SymbolOrderBook.cs
index d6ee7b4..cedb443 100644
--- a/CryptoExchange.Net/OrderBook/SymbolOrderBook.cs
+++ b/CryptoExchange.Net/OrderBook/SymbolOrderBook.cs
@@ -9,6 +9,7 @@ using System.Threading.Tasks;
using CryptoExchange.Net.Interfaces;
using CryptoExchange.Net.Logging.Extensions;
using CryptoExchange.Net.Objects;
+using CryptoExchange.Net.Objects.Errors;
using CryptoExchange.Net.Objects.Options;
using CryptoExchange.Net.Objects.Sockets;
using Microsoft.Extensions.Logging;
@@ -549,7 +550,7 @@ namespace CryptoExchange.Net.OrderBook
return new CallResult(new CancellationRequestedError());
if (DateTime.UtcNow - startWait > timeout)
- return new CallResult(new ServerError("Timeout while waiting for data"));
+ return new CallResult(new ServerError(new ErrorInfo(ErrorType.OrderBookTimeout, "Timeout while waiting for data")));
try
{
diff --git a/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/EndpointOptions.cs b/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/EndpointOptions.cs
index 0113ac4..8d4d552 100644
--- a/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/EndpointOptions.cs
+++ b/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/EndpointOptions.cs
@@ -58,19 +58,19 @@ namespace CryptoExchange.Net.SharedApis
public virtual Error? ValidateRequest(string exchange, ExchangeParameters? exchangeParameters, TradingMode? tradingMode, TradingMode[] supportedTradingModes)
{
if (tradingMode != null && !supportedTradingModes.Contains(tradingMode.Value))
- return new ArgumentError($"ApiType.{tradingMode} is not supported, supported types: {string.Join(", ", supportedTradingModes)}");
+ return ArgumentError.Invalid("TradingMode", $"TradingMode.{tradingMode} is not supported, supported types: {string.Join(", ", supportedTradingModes)}");
foreach (var param in RequiredExchangeParameters)
{
if (!string.IsNullOrEmpty(param.Name))
{
if (ExchangeParameters.HasValue(exchangeParameters, exchange, param.Name!, param.ValueType) != true)
- return new ArgumentError($"Required exchange parameter `{param.Name}` for exchange `{exchange}` is missing or has incorrect type. Expected type is {param.ValueType.Name}. Example: {param.ExampleValue}");
+ return ArgumentError.Invalid(param.Name!, $"Required exchange parameter `{param.Name}` for exchange `{exchange}` is missing or has incorrect type. Expected type is {param.ValueType.Name}. Example: {param.ExampleValue}");
}
else
{
if (param.Names!.All(x => ExchangeParameters.HasValue(exchangeParameters, exchange, x, param.ValueType) != true))
- return new ArgumentError($"One of exchange parameters `{string.Join(", ", param.Names!)}` for exchange `{exchange}` should be provided. Example: {param.ExampleValue}");
+ return ArgumentError.Invalid(string.Join("/", param.Names!), $"One of exchange parameters `{string.Join(", ", param.Names!)}` for exchange `{exchange}` should be provided. Example: {param.ExampleValue}");
}
}
@@ -140,12 +140,12 @@ namespace CryptoExchange.Net.SharedApis
if (!string.IsNullOrEmpty(param.Name))
{
if (typeof(T).GetProperty(param.Name)!.GetValue(request, null) == null)
- return new ArgumentError($"Required optional parameter `{param.Name}` for exchange `{exchange}` is missing. Example: {param.ExampleValue}");
+ return ArgumentError.Invalid(param.Name!, $"Required optional parameter `{param.Name}` for exchange `{exchange}` is missing. Example: {param.ExampleValue}");
}
else
{
if (param.Names!.All(x => typeof(T).GetProperty(param.Name!)!.GetValue(request, null) == null))
- return new ArgumentError($"One of optional parameters `{string.Join(", ", param.Names!)}` for exchange `{exchange}` should be provided. Example: {param.ExampleValue}");
+ return ArgumentError.Invalid(string.Join("/", param.Names!), $"One of optional parameters `{string.Join(", ", param.Names!)}` for exchange `{exchange}` should be provided. Example: {param.ExampleValue}");
}
}
@@ -155,10 +155,10 @@ namespace CryptoExchange.Net.SharedApis
if (symbolsRequest.Symbols != null)
{
if (!SupportsMultipleSymbols)
- return new ArgumentError($"Only a single symbol parameter is allowed, multiple symbols are not supported");
+ return ArgumentError.Invalid(nameof(SharedSymbolRequest.Symbols), $"Only a single symbol parameter is allowed, multiple symbols are not supported");
if (symbolsRequest.Symbols.Length > MaxSymbolCount)
- return new ArgumentError($"Max number of symbols is {MaxSymbolCount} but {symbolsRequest.Symbols.Length} were passed");
+ return ArgumentError.Invalid(nameof(SharedSymbolRequest.Symbols), $"Max number of symbols is {MaxSymbolCount} but {symbolsRequest.Symbols.Length} were passed");
}
}
diff --git a/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/GetClosedOrdersOptions.cs b/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/GetClosedOrdersOptions.cs
index c567caa..d273f1a 100644
--- a/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/GetClosedOrdersOptions.cs
+++ b/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/GetClosedOrdersOptions.cs
@@ -25,7 +25,7 @@ namespace CryptoExchange.Net.SharedApis
public override Error? ValidateRequest(string exchange, GetClosedOrdersRequest request, TradingMode? tradingMode, TradingMode[] supportedApiTypes)
{
if (TimeFilterSupported && request.StartTime != null)
- return new ArgumentError($"Time filter is not supported");
+ return ArgumentError.Invalid(nameof(GetClosedOrdersRequest.StartTime), $"Time filter is not supported");
return base.ValidateRequest(exchange, request, tradingMode, supportedApiTypes);
}
diff --git a/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/GetDepositsOptions.cs b/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/GetDepositsOptions.cs
index ec30c34..873f144 100644
--- a/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/GetDepositsOptions.cs
+++ b/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/GetDepositsOptions.cs
@@ -25,7 +25,7 @@ namespace CryptoExchange.Net.SharedApis
public override Error? ValidateRequest(string exchange, GetDepositsRequest request, TradingMode? tradingMode, TradingMode[] supportedApiTypes)
{
if (TimeFilterSupported && request.StartTime != null)
- return new ArgumentError($"Time filter is not supported");
+ return ArgumentError.Invalid(nameof(GetDepositsRequest.StartTime), $"Time filter is not supported");
return base.ValidateRequest(exchange, request, tradingMode, supportedApiTypes);
}
diff --git a/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/GetKlinesOptions.cs b/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/GetKlinesOptions.cs
index 1a1b97e..a5e496f 100644
--- a/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/GetKlinesOptions.cs
+++ b/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/GetKlinesOptions.cs
@@ -67,23 +67,23 @@ namespace CryptoExchange.Net.SharedApis
public override Error? ValidateRequest(string exchange, GetKlinesRequest request, TradingMode? tradingMode, TradingMode[] supportedApiTypes)
{
if (!IsSupported(request.Interval))
- return new ArgumentError("Interval not supported");
+ return ArgumentError.Invalid(nameof(GetKlinesRequest.Interval), "Interval not supported");
if (MaxAge.HasValue && request.StartTime < DateTime.UtcNow.Add(-MaxAge.Value))
- return new ArgumentError($"Only the most recent {MaxAge} klines are available");
+ return ArgumentError.Invalid(nameof(GetKlinesRequest.StartTime), $"Only the most recent {MaxAge} klines are available");
if (request.Limit > MaxLimit)
- return new ArgumentError($"Only {MaxLimit} klines can be retrieved per request");
+ return ArgumentError.Invalid(nameof(GetKlinesRequest.Limit), $"Only {MaxLimit} klines can be retrieved per request");
if (MaxTotalDataPoints.HasValue)
{
if (request.Limit > MaxTotalDataPoints.Value)
- return new ArgumentError($"Only the most recent {MaxTotalDataPoints} klines are available");
+ return ArgumentError.Invalid(nameof(GetKlinesRequest.Limit), $"Only the most recent {MaxTotalDataPoints} klines are available");
if (request.StartTime.HasValue == true)
{
if (((request.EndTime ?? DateTime.UtcNow) - request.StartTime.Value).TotalSeconds / (int)request.Interval > MaxTotalDataPoints.Value)
- return new ArgumentError($"Only the most recent {MaxTotalDataPoints} klines are available, time filter failed");
+ return ArgumentError.Invalid(nameof(GetKlinesRequest.StartTime), $"Only the most recent {MaxTotalDataPoints} klines are available, time filter failed");
}
}
diff --git a/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/GetOrderBookOptions.cs b/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/GetOrderBookOptions.cs
index 7c117ee..a7ff8e9 100644
--- a/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/GetOrderBookOptions.cs
+++ b/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/GetOrderBookOptions.cs
@@ -49,13 +49,13 @@ namespace CryptoExchange.Net.SharedApis
return null;
if (MaxLimit.HasValue && request.Limit.Value > MaxLimit)
- return new ArgumentError($"Max limit is {MaxLimit}");
+ return ArgumentError.Invalid(nameof(GetOrderBookRequest.Limit), $"Max limit is {MaxLimit}");
if (MinLimit.HasValue && request.Limit.Value < MinLimit)
- return new ArgumentError($"Min limit is {MaxLimit}");
+ return ArgumentError.Invalid(nameof(GetOrderBookRequest.Limit), $"Min limit is {MaxLimit}");
if (SupportedLimits != null && !SupportedLimits.Contains(request.Limit.Value))
- return new ArgumentError($"Limit should be one of " + string.Join(", ", SupportedLimits));
+ return ArgumentError.Invalid(nameof(GetOrderBookRequest.Limit), $"Limit should be one of " + string.Join(", ", SupportedLimits));
return base.ValidateRequest(exchange, request, tradingMode, supportedApiTypes);
}
diff --git a/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/GetRecentTradesOptions.cs b/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/GetRecentTradesOptions.cs
index a6b43ec..815aa29 100644
--- a/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/GetRecentTradesOptions.cs
+++ b/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/GetRecentTradesOptions.cs
@@ -25,7 +25,7 @@ namespace CryptoExchange.Net.SharedApis
public Error? Validate(GetRecentTradesRequest request)
{
if (request.Limit > MaxLimit)
- return new ArgumentError($"Only the most recent {MaxLimit} trades are available");
+ return ArgumentError.Invalid(nameof(GetRecentTradesRequest.Limit), $"Only the most recent {MaxLimit} trades are available");
return null;
}
diff --git a/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/GetTradeHistoryOptions.cs b/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/GetTradeHistoryOptions.cs
index 018eccb..d07972f 100644
--- a/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/GetTradeHistoryOptions.cs
+++ b/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/GetTradeHistoryOptions.cs
@@ -25,7 +25,7 @@ namespace CryptoExchange.Net.SharedApis
public override Error? ValidateRequest(string exchange, GetTradeHistoryRequest request, TradingMode? tradingMode, TradingMode[] supportedApiTypes)
{
if (MaxAge.HasValue && request.StartTime < DateTime.UtcNow.Add(-MaxAge.Value))
- return new ArgumentError($"Only the most recent {MaxAge} trades are available");
+ return ArgumentError.Invalid(nameof(GetTradeHistoryRequest.StartTime), $"Only the most recent {MaxAge} trades are available");
return base.ValidateRequest(exchange, request, tradingMode, supportedApiTypes);
}
diff --git a/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/GetWithdrawalsOptions.cs b/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/GetWithdrawalsOptions.cs
index 1806c6a..f6f3c7c 100644
--- a/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/GetWithdrawalsOptions.cs
+++ b/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/GetWithdrawalsOptions.cs
@@ -25,7 +25,7 @@ namespace CryptoExchange.Net.SharedApis
public override Error? ValidateRequest(string exchange, GetWithdrawalsRequest request, TradingMode? tradingMode, TradingMode[] supportedApiTypes)
{
if (TimeFilterSupported && request.StartTime != null)
- return new ArgumentError($"Time filter is not supported");
+ return ArgumentError.Invalid(nameof(GetWithdrawalsRequest.StartTime), $"Time filter is not supported");
return base.ValidateRequest(exchange, request, tradingMode, supportedApiTypes);
}
diff --git a/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/PlaceFuturesOrderOptions.cs b/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/PlaceFuturesOrderOptions.cs
index f89d1d3..8a7c9b1 100644
--- a/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/PlaceFuturesOrderOptions.cs
+++ b/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/PlaceFuturesOrderOptions.cs
@@ -36,16 +36,16 @@ namespace CryptoExchange.Net.SharedApis
SharedQuantitySupport quantitySupport)
{
if (!SupportsTpSl && (request.StopLossPrice != null || request.TakeProfitPrice != null))
- return new ArgumentError("Tp/Sl parameters not supported");
+ return ArgumentError.Invalid(nameof(PlaceFuturesOrderRequest.StopLossPrice) + " / " + nameof(PlaceFuturesOrderRequest.TakeProfitPrice), "Tp/Sl parameters not supported");
if (request.OrderType == SharedOrderType.Other)
throw new ArgumentException("OrderType can't be `Other`", nameof(request.OrderType));
if (!supportedOrderTypes.Contains(request.OrderType))
- return new ArgumentError("Order type not supported");
+ return ArgumentError.Invalid(nameof(PlaceFuturesOrderRequest.OrderType), "Order type not supported");
if (request.TimeInForce != null && !supportedTimeInForce.Contains(request.TimeInForce.Value))
- return new ArgumentError("Order time in force not supported");
+ return ArgumentError.Invalid(nameof(PlaceFuturesOrderRequest.TimeInForce), "Order time in force not supported");
var quantityError = quantitySupport.Validate(request.Side, request.OrderType, request.Quantity);
if (quantityError != null)
diff --git a/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/PlaceSpotOrderOptions.cs b/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/PlaceSpotOrderOptions.cs
index 75c61b7..7e3c2e3 100644
--- a/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/PlaceSpotOrderOptions.cs
+++ b/CryptoExchange.Net/SharedApis/Models/Options/Endpoints/PlaceSpotOrderOptions.cs
@@ -34,10 +34,10 @@ namespace CryptoExchange.Net.SharedApis
throw new ArgumentException("OrderType can't be `Other`", nameof(request.OrderType));
if (!supportedOrderTypes.Contains(request.OrderType))
- return new ArgumentError("Order type not supported");
+ return ArgumentError.Invalid(nameof(PlaceSpotOrderRequest.OrderType), "Order type not supported");
if (request.TimeInForce != null && !supportedTimeInForce.Contains(request.TimeInForce.Value))
- return new ArgumentError("Order time in force not supported");
+ return ArgumentError.Invalid(nameof(PlaceSpotOrderRequest.TimeInForce), "Order time in force not supported");
var quantityError = quantitySupport.Validate(request.Side, request.OrderType, request.Quantity);
if (quantityError != null)
diff --git a/CryptoExchange.Net/SharedApis/Models/Options/Subscriptions/SubscribeKlineOptions.cs b/CryptoExchange.Net/SharedApis/Models/Options/Subscriptions/SubscribeKlineOptions.cs
index f6c4d7f..826c964 100644
--- a/CryptoExchange.Net/SharedApis/Models/Options/Subscriptions/SubscribeKlineOptions.cs
+++ b/CryptoExchange.Net/SharedApis/Models/Options/Subscriptions/SubscribeKlineOptions.cs
@@ -60,7 +60,7 @@ namespace CryptoExchange.Net.SharedApis
public override Error? ValidateRequest(string exchange, SubscribeKlineRequest request, TradingMode? tradingMode, TradingMode[] supportedApiTypes)
{
if (!IsSupported(request.Interval))
- return new ArgumentError("Interval not supported");
+ return ArgumentError.Invalid(nameof(SubscribeKlineRequest.Interval), "Interval not supported");
return base.ValidateRequest(exchange, request, tradingMode, supportedApiTypes);
}
diff --git a/CryptoExchange.Net/SharedApis/Models/Options/Subscriptions/SubscribeOrderBookOptions.cs b/CryptoExchange.Net/SharedApis/Models/Options/Subscriptions/SubscribeOrderBookOptions.cs
index bd3c36b..c2baed2 100644
--- a/CryptoExchange.Net/SharedApis/Models/Options/Subscriptions/SubscribeOrderBookOptions.cs
+++ b/CryptoExchange.Net/SharedApis/Models/Options/Subscriptions/SubscribeOrderBookOptions.cs
@@ -29,7 +29,7 @@ namespace CryptoExchange.Net.SharedApis
public override Error? ValidateRequest(string exchange, SubscribeOrderBookRequest request, TradingMode? tradingMode, TradingMode[] supportedApiTypes)
{
if (request.Limit != null && !SupportedLimits.Contains(request.Limit.Value))
- return new ArgumentError("Limit not supported");
+ return ArgumentError.Invalid(nameof(SubscribeOrderBookRequest.Limit), "Limit not supported");
return base.ValidateRequest(exchange, request, tradingMode, supportedApiTypes);
}
diff --git a/CryptoExchange.Net/SharedApis/Models/SharedQuantitySupport.cs b/CryptoExchange.Net/SharedApis/Models/SharedQuantitySupport.cs
index aea1d5a..709f441 100644
--- a/CryptoExchange.Net/SharedApis/Models/SharedQuantitySupport.cs
+++ b/CryptoExchange.Net/SharedApis/Models/SharedQuantitySupport.cs
@@ -84,16 +84,16 @@ namespace CryptoExchange.Net.SharedApis
return null;
if (supportedType == SharedQuantityType.BaseAndQuoteAsset && quantity != null && quantity.QuantityInBaseAsset == null && quantity.QuantityInQuoteAsset == null)
- return new ArgumentError($"Quantity for {side}.{type} required in base or quote asset");
+ return ArgumentError.Invalid("Quantity", $"Quantity for {side}.{type} required in base or quote asset");
if (supportedType == SharedQuantityType.QuoteAsset && quantity != null && quantity.QuantityInQuoteAsset == null)
- return new ArgumentError($"Quantity for {side}.{type} required in quote asset");
+ return ArgumentError.Invalid("Quantity", $"Quantity for {side}.{type} required in quote asset");
if (supportedType == SharedQuantityType.BaseAsset && quantity != null && quantity.QuantityInBaseAsset == null && quantity.QuantityInContracts == null)
- return new ArgumentError($"Quantity for {side}.{type} required in base asset");
+ return ArgumentError.Invalid("Quantity", $"Quantity for {side}.{type} required in base asset");
if (supportedType == SharedQuantityType.Contracts && quantity != null && quantity.QuantityInContracts == null)
- return new ArgumentError($"Quantity for {side}.{type} required in contracts");
+ return ArgumentError.Invalid("Quantity", $"Quantity for {side}.{type} required in contracts");
return null;
}
diff --git a/CryptoExchange.Net/Sockets/Query.cs b/CryptoExchange.Net/Sockets/Query.cs
index a103200..07fd646 100644
--- a/CryptoExchange.Net/Sockets/Query.cs
+++ b/CryptoExchange.Net/Sockets/Query.cs
@@ -219,7 +219,7 @@ namespace CryptoExchange.Net.Sockets
return;
Completed = true;
- Result = new CallResult(new CancellationRequestedError(null, "Query timeout", null));
+ Result = new CallResult(new TimeoutError());
ContinueAwaiter?.Set();
_event.Set();
}
diff --git a/CryptoExchange.Net/Sockets/SocketConnection.cs b/CryptoExchange.Net/Sockets/SocketConnection.cs
index 3dc3fee..64e0f81 100644
--- a/CryptoExchange.Net/Sockets/SocketConnection.cs
+++ b/CryptoExchange.Net/Sockets/SocketConnection.cs
@@ -477,7 +477,7 @@ namespace CryptoExchange.Net.Sockets
if (!accessor.IsValid && !ApiClient.ProcessUnparsableMessages)
{
- _logger.FailedToParse(SocketId, result.Error!.Message);
+ _logger.FailedToParse(SocketId, result.Error!.Message ?? result.Error!.ErrorDescription!);
return;
}
@@ -765,7 +765,7 @@ namespace CryptoExchange.Net.Sockets
public virtual async Task SendAndWaitQueryAsync(Query query, AsyncResetEvent? continueEvent = null, CancellationToken ct = default)
{
await SendAndWaitIntAsync(query, continueEvent, ct).ConfigureAwait(false);
- return query.Result ?? new CallResult(new ServerError("Timeout"));
+ return query.Result ?? new CallResult(new TimeoutError());
}
///
@@ -779,7 +779,7 @@ namespace CryptoExchange.Net.Sockets
public virtual async Task> SendAndWaitQueryAsync(Query query, AsyncResetEvent? continueEvent = null, CancellationToken ct = default)
{
await SendAndWaitIntAsync(query, continueEvent, ct).ConfigureAwait(false);
- return query.TypedResult ?? new CallResult(new ServerError("Timeout"));
+ return query.TypedResult ?? new CallResult(new TimeoutError());
}
private async Task SendAndWaitIntAsync(Query query, AsyncResetEvent? continueEvent, CancellationToken ct = default)
diff --git a/CryptoExchange.Net/Trackers/Klines/KlineTracker.cs b/CryptoExchange.Net/Trackers/Klines/KlineTracker.cs
index 585a2c9..cfc4c33 100644
--- a/CryptoExchange.Net/Trackers/Klines/KlineTracker.cs
+++ b/CryptoExchange.Net/Trackers/Klines/KlineTracker.cs
@@ -184,7 +184,7 @@ namespace CryptoExchange.Net.Trackers.Klines
if (!subResult)
{
- _logger.KlineTrackerStartFailed(SymbolName, subResult.Error!.Message, subResult.Error.Exception);
+ _logger.KlineTrackerStartFailed(SymbolName, subResult.Error!.Message ?? subResult.Error!.ErrorDescription!, subResult.Error.Exception);
Status = SyncStatus.Disconnected;
return subResult;
}
diff --git a/CryptoExchange.Net/Trackers/Trades/TradeTracker.cs b/CryptoExchange.Net/Trackers/Trades/TradeTracker.cs
index 0b9bf77..5d50e1e 100644
--- a/CryptoExchange.Net/Trackers/Trades/TradeTracker.cs
+++ b/CryptoExchange.Net/Trackers/Trades/TradeTracker.cs
@@ -207,7 +207,7 @@ namespace CryptoExchange.Net.Trackers.Trades
if (!subResult)
{
- _logger.TradeTrackerStartFailed(SymbolName, subResult.Error!.Message, subResult.Error.Exception);
+ _logger.TradeTrackerStartFailed(SymbolName, subResult.Error!.Message ?? subResult.Error!.ErrorDescription!, subResult.Error.Exception);
Status = SyncStatus.Disconnected;
return subResult;
}