Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v1
with:
dotnet-version: 9.0.x
dotnet-version: 10.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
Expand Down
8 changes: 4 additions & 4 deletions Coinbase.Net.UnitTests/Coinbase.Net.UnitTests.csproj
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>

<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0"></PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1"></PackageReference>
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="NUnit" Version="4.2.2"></PackageReference>
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0"></PackageReference>
<PackageReference Include="NUnit" Version="4.4.0"></PackageReference>
<PackageReference Include="NUnit3TestAdapter" Version="6.0.0"></PackageReference>
</ItemGroup>

<ItemGroup>
Expand Down
12 changes: 7 additions & 5 deletions Coinbase.Net.UnitTests/CoinbaseSocketIntegrationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public CoinbaseSocketIntegrationTests()
{
}

public override CoinbaseSocketClient GetClient(ILoggerFactory loggerFactory)
public override CoinbaseSocketClient GetClient(ILoggerFactory loggerFactory, bool useUpdatedDeserialization)
{
var key = Environment.GetEnvironmentVariable("APIKEY");
var sec = Environment.GetEnvironmentVariable("APISECRET");
Expand All @@ -28,15 +28,17 @@ public override CoinbaseSocketClient GetClient(ILoggerFactory loggerFactory)
return new CoinbaseSocketClient(Options.Create(new CoinbaseSocketOptions
{
OutputOriginalData = true,
UseUpdatedDeserialization = useUpdatedDeserialization,
ApiCredentials = Authenticated ? new CryptoExchange.Net.Authentication.ApiCredentials(key, sec) : null
}), loggerFactory);
}

[Test]
public async Task TestSubscriptions()
[TestCase(false)]
[TestCase(true)]
public async Task TestSubscriptions(bool useUpdatedDeserialization)
{
await RunAndCheckUpdate<CoinbaseTicker>((client, updateHandler) => client.AdvancedTradeApi.SubscribeToFuturesBalanceUpdatesAsync(default , default), false, true);
await RunAndCheckUpdate<CoinbaseTicker>((client, updateHandler) => client.AdvancedTradeApi.SubscribeToTickerUpdatesAsync("ETH-USD", updateHandler, default), true, false);
await RunAndCheckUpdate<CoinbaseFuturesBalance>(useUpdatedDeserialization , (client, updateHandler) => client.AdvancedTradeApi.SubscribeToFuturesBalanceUpdatesAsync(updateHandler, default), false, true);
await RunAndCheckUpdate<CoinbaseTicker>(useUpdatedDeserialization , (client, updateHandler) => client.AdvancedTradeApi.SubscribeToTickerUpdatesAsync("ETH-USD", updateHandler, default), true, false);
}
}
}
47 changes: 39 additions & 8 deletions Coinbase.Net.UnitTests/SocketSubscriptionTests.cs
Original file line number Diff line number Diff line change
@@ -1,23 +1,54 @@
using CryptoExchange.Net.Testing;
using NUnit.Framework;
using System.Threading.Tasks;
using Coinbase.Net.Clients;
using Coinbase.Net.Objects.Models;
using Coinbase.Net.Objects.Options;
using CryptoExchange.Net.Authentication;
using CryptoExchange.Net.Objects;
using CryptoExchange.Net.Testing;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NUnit.Framework;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Coinbase.Net.UnitTests
{
[TestFixture]
public class SocketSubscriptionTests
{
[Test]
public async Task ValidateAdvancedTradeExchangeDataSubscriptions()
[TestCase(false)]
[TestCase(true)]
public async Task ValidateConcurrentSubscriptions(bool newDeserialization)
{
var logger = new LoggerFactory();
logger.AddProvider(new TraceLoggerProvider());

var client = new CoinbaseSocketClient(Options.Create(new CoinbaseSocketOptions
{
OutputOriginalData = true,
UseUpdatedDeserialization = newDeserialization
}), logger);

var tester = new SocketSubscriptionValidator<CoinbaseSocketClient>(client, "Subscriptions/AdvancedTrade", "wss://advanced-trade-ws.coinbase.com", "events.0");
await tester.ValidateConcurrentAsync<CoinbaseStreamKline[]>(
(client, handler) => client.AdvancedTradeApi.SubscribeToKlineUpdatesAsync("ETH-USDT", handler),
(client, handler) => client.AdvancedTradeApi.SubscribeToKlineUpdatesAsync("BTC-USDT", handler),
"Concurrent");
}

#warning add ExchangeApi

Check warning on line 38 in Coinbase.Net.UnitTests/SocketSubscriptionTests.cs

View workflow job for this annotation

GitHub Actions / build

#warning: 'add ExchangeApi'

Check warning on line 38 in Coinbase.Net.UnitTests/SocketSubscriptionTests.cs

View workflow job for this annotation

GitHub Actions / build

#warning: 'add ExchangeApi'
[TestCase(false)]
[TestCase(true)]
public async Task ValidateAdvancedTradeExchangeDataSubscriptions(bool newDeserialization)
{
var client = new CoinbaseSocketClient(opts =>
var logger = new LoggerFactory();
logger.AddProvider(new TraceLoggerProvider());

var client = new CoinbaseSocketClient(Options.Create(new CoinbaseSocketOptions
{
opts.ApiCredentials = new ApiCredentials("123", "-----BEGIN EC PRIVATE KEY-----\r\nMHcCAQEEIGaopmcUKDBihelMJbKUyRmaR6F3Eo90EZaqZJ3/mBr0oAoGCCqGSM49\r\nAwEHoUQDQgAEnYaxPG+o57xM5o/M5QNn0ocwlw12ZNVWFEo9tKDQ7Jz5Gz/0eMcP\r\nmEhm5msFFpWgrY0/T92MfwByuaLws/rM3w==\r\n-----END EC PRIVATE KEY-----");
});
ApiCredentials = new ApiCredentials("123", "-----BEGIN EC PRIVATE KEY-----\r\nMHcCAQEEIGaopmcUKDBihelMJbKUyRmaR6F3Eo90EZaqZJ3/mBr0oAoGCCqGSM49\r\nAwEHoUQDQgAEnYaxPG+o57xM5o/M5QNn0ocwlw12ZNVWFEo9tKDQ7Jz5Gz/0eMcP\r\nmEhm5msFFpWgrY0/T92MfwByuaLws/rM3w==\r\n-----END EC PRIVATE KEY-----"),
OutputOriginalData = true,
UseUpdatedDeserialization = newDeserialization
}), logger);
var tester = new SocketSubscriptionValidator<CoinbaseSocketClient>(client, "Subscriptions/AdvancedTrade", "wss://advanced-trade-ws.coinbase.com", "events.0");
await tester.ValidateAsync<CoinbaseHeartbeat>((client, handler) => client.AdvancedTradeApi.SubscribeToHeartbeatUpdatesAsync(handler), "Heartbeat");
await tester.ValidateAsync<CoinbaseTrade[]>((client, handler) => client.AdvancedTradeApi.SubscribeToTradeUpdatesAsync("ETH-USDT", handler), "Trades", "events.0.trades");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
>1 { "type": "subscribe", "product_ids": ["ETH-USDT"], "channel": "candles", "jwt": "|*|" }
>2 { "type": "subscribe", "product_ids": ["BTC-USDT"], "channel": "candles", "jwt": "|*|" }
<1 {"channel":"subscriptions","client_id":"","timestamp":"2024-10-04T07:13:08.600625889Z","sequence_num":1,"events":[{"subscriptions":{"candles":["ETH-USDT"]}}]}
<2 {"channel":"subscriptions","client_id":"","timestamp":"2024-10-04T07:13:08.600625889Z","sequence_num":1,"events":[{"subscriptions":{"candles":["BTC-USDT"]}}]}
=1 { "channel": "candles", "client_id": "", "timestamp": "2023-06-09T20:19:35.39625135Z", "sequence_num": 0, "events": [{ "type": "snapshot", "candles": [{ "product_id": "ETH-USDT" }]}]}
=2 { "channel": "candles", "client_id": "", "timestamp": "2023-06-09T20:19:35.39625135Z", "sequence_num": 0, "events": [{ "type": "snapshot", "candles": [{ "product_id": "BTC-USDT" }]}]}
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
using CryptoExchange.Net;
using Coinbase.Net.Clients.MessageHandlers;
using Coinbase.Net.Interfaces.Clients.AdvancedTradeApi;
using Coinbase.Net.Objects.Options;
using CryptoExchange.Net.Authentication;
using CryptoExchange.Net.Clients;
using CryptoExchange.Net.Converters.MessageParsing;
using CryptoExchange.Net.Converters.MessageParsing.DynamicConverters;
using CryptoExchange.Net.Converters.SystemTextJson;
using CryptoExchange.Net.Interfaces;
using CryptoExchange.Net.Objects;
using CryptoExchange.Net.Objects.Errors;
using CryptoExchange.Net.SharedApis;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using Coinbase.Net.Objects.Options;
using CryptoExchange.Net.Clients;
using CryptoExchange.Net.Converters.SystemTextJson;
using CryptoExchange.Net.Interfaces;
using CryptoExchange.Net.SharedApis;
using CryptoExchange.Net.Converters.MessageParsing;
using System.Reflection;
using Coinbase.Net.Interfaces.Clients.AdvancedTradeApi;
using System.Linq;
using CryptoExchange.Net.Objects.Errors;

namespace Coinbase.Net.Clients.AdvancedTradeApi
{
Expand All @@ -26,6 +27,7 @@ internal partial class CoinbaseRestClientAdvancedTradeApi : RestApiClient, ICoin
#region fields
internal static TimeSyncState _timeSyncState = new TimeSyncState("Advanced Trade Api");

protected override IRestMessageHandler MessageHandler { get; } = new CoinbaseRestMessageHandler(CoinbaseErrors.Errors);
protected override ErrorMapping ErrorMapping => CoinbaseErrors.Errors;
#endregion

Expand Down Expand Up @@ -82,45 +84,6 @@ internal async Task<WebCallResult<T>> SendToAddressAsync<T>(string baseAddress,
return await base.SendAsync<T>(baseAddress, definition, parameters, cancellationToken, null, weight).ConfigureAwait(false);
}

protected override ServerRateLimitError ParseRateLimitResponse(int httpStatusCode, KeyValuePair<string, string[]>[] responseHeaders, IMessageAccessor accessor)
{
var reset = responseHeaders.SingleOrDefault(x => x.Key.Equals("x-ratelimit-reset", StringComparison.InvariantCultureIgnoreCase));
if (reset.Key == null)
return base.ParseRateLimitResponse(httpStatusCode, responseHeaders, accessor);

if (!int.TryParse(reset.Value.Single(), out var seconds))
return base.ParseRateLimitResponse(httpStatusCode, responseHeaders, accessor);

var error = new ServerRateLimitError(accessor.GetOriginalString());
error.RetryAfter = DateTime.UtcNow.AddSeconds(seconds);
return error;
}

protected override Error ParseErrorResponse(int httpStatusCode, KeyValuePair<string, string[]>[] responseHeaders, IMessageAccessor accessor, Exception? exception)
{
if (!accessor.IsValid)
{
if (httpStatusCode == 401)
return new ServerError(new ErrorInfo(ErrorType.Unauthorized, "Unauthorized"));

return new ServerError(ErrorInfo.Unknown, exception: exception);
}

var error = accessor.GetValue<string>(MessagePath.Get().Property("error"));
if (error == null)
{
var errorId = accessor.GetValue<string?>(MessagePath.Get().Property("errors").Index(0).Property("id"));
var errorMsg = accessor.GetValue<string?>(MessagePath.Get().Property("errors").Index(0).Property("message"));
if (errorId != null)
return new ServerError(errorId, GetErrorInfo(errorId, errorMsg), exception);

return new ServerError(ErrorInfo.Unknown, exception: exception);
}

var msg = accessor.GetValue<string>(MessagePath.Get().Property("message"));
return new ServerError(error, GetErrorInfo(error, msg), exception);
}

/// <inheritdoc />
protected override Task<WebCallResult<DateTime>> GetServerTimestampAsync()
=> ExchangeData.GetServerTimeAsync();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
using CryptoExchange.Net.Objects;
using CryptoExchange.Net;
using Coinbase.Net.Enums;
using System.Drawing;
using CryptoExchange.Net.Objects.Errors;

namespace Coinbase.Net.Clients.AdvancedTradeApi
Expand Down Expand Up @@ -1242,7 +1241,7 @@ async Task<ExchangeWebResult<SharedKline[]>> IKlineRestClient.GetKlinesAsync(Get
nextToken = new DateTimeToken(minOpenTime.AddSeconds(-(int)(interval - 1)));
}

return result.AsExchangeResult<SharedKline[]>(Exchange, request.Symbol!.TradingMode, result.Data.Reverse().Select(x =>
return result.AsExchangeResult<SharedKline[]>(Exchange, request.Symbol!.TradingMode, result.Data.AsEnumerable().Reverse().Select(x =>
new SharedKline(request.Symbol, symbol, x.OpenTime, x.ClosePrice, x.HighPrice, x.LowPrice, x.OpenPrice, x.Volume)).ToArray(), nextToken);
}

Expand Down
Loading