-
-
Notifications
You must be signed in to change notification settings - Fork 94
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Any plans to support coinbase advanced trading? #84
Comments
@bchavez > Looks like the advanced trading api is now up and running. Any plans to convert over or make a different api? Would like to, e.g., list products and find out which are/aren't tradable as can be done w the v3 API that uses the same sign on process. Thanks. |
@bchavez Me too. As of 30 Nov 2023, Sign in With Coinbase (the API this library calls) took away the |
No plans to support Coinbase's Advanced Trading APIs at the moment unless we get a significant monetary support stipend from Coinbase. Supporting Coinbase's APIs has been a very painful process in the face of all their API version changes. |
Indeed. |
Hi @bchavez I am doing some further digging using a quick and dirty Coinbase API client I whipped up at home (that signs requests correctly and such) to get the raw JSON of a Am doing some unit tests now...will keep you posted. I am hoping that the |
@bchavez Good news! I did some more testing and I discovered, that yes, in fact, one can pass a URL such as var requestPath =
$"/v2/accounts/{accountId}/transactions/{transactionId}?expand=all"; Note the When we pass {
"data": {
"id": "fc988949-d761-510b-a862-9bcb45ea85d9",
"type": "buy",
"status": "completed",
"amount": {
"amount": "0.00060521",
"currency": "BTC"
},
"native_amount": {
"amount": "20.00",
"currency": "USD"
},
"description": null,
"created_at": "2023-04-11T19:14:01Z",
"updated_at": "2023-04-13T05:44:31Z",
"resource": "transaction",
"resource_path": "/v2/accounts/67eed12a-ddd2-569e-b262-54a8287fcfb1/transactions/fc988949-d761-510b-a862-9bcb45ea85d9",
"instant_exchange": false,
"buy": {
"id": "b27a7cde-e784-5848-a9dc-a5ca55e94221",
"status": "completed",
"transaction": {
"id": "fc988949-d761-510b-a862-9bcb45ea85d9",
"resource": "transaction",
"resource_path": "/v2/accounts/67eed12a-ddd2-569e-b262-54a8287fcfb1/transactions/fc988949-d761-510b-a862-9bcb45ea85d9"
},
"user_reference": "7MYRUATE",
"created_at": "2023-04-11T19:13:49Z",
"updated_at": "2023-04-11T19:53:58Z",
"resource": "buy",
"resource_path": "/v2/accounts/67eed12a-ddd2-569e-b262-54a8287fcfb1/buys/b27a7cde-e784-5848-a9dc-a5ca55e94221",
"payment_method": {
"id": "d9e7cdd2-eebc-51b3-9394-cecd88db1cc8",
"resource": "payment_method",
"resource_path": "/v2/payment-methods/d9e7cdd2-eebc-51b3-9394-cecd88db1cc8"
},
"committed": true,
"payout_at": "2023-04-11T19:13:48Z",
"instant": true,
"fee": {
"amount": "1.49",
"currency": "USD"
},
"amount": {
"amount": "0.00060521",
"currency": "BTC"
},
"total": {
"amount": "20.00",
"currency": "USD"
},
"subtotal": {
"amount": "18.51",
"currency": "USD"
},
"unit_price": {
"amount": "30584.43",
"currency": "USD",
"scale": 2
},
"hold_until": "2023-04-17T23:00:00Z",
"hold_days": 7,
"idem": "dfe26a85-92b0-42f6-adab-aeea9a8c413a",
"next_step": null,
"is_first_buy": false,
"requires_completion_step": false
},
"details": {
"title": "Bought Bitcoin",
"subtitle": "Using NAVY FEDERAL CREDIT UNION ******8602",
"header": "Bought 0.00060521 BTC ($20.00)",
"health": "positive",
"payment_method_name": "NAVY FEDERAL CREDIT UNION ******8602"
},
"hide_native_amount": false
},
"warnings": [
{
"id": "missing_version",
"message": "Please supply API version (YYYY-MM-DD) as CB-VERSION header",
"url": "https://developers.coinbase.com/api#versioning"
}
]
} Listing 1. JSON returned with an expanded
We can see that we now have the transaction but augmented with I respectfully suggest that your code be modified to always expand the Or, could I do it myself and then submit a PR? |
I ran the JSON through public partial class Transaction
{
[JsonProperty("data")]
public Data Data { get; set; }
[JsonProperty("errors")]
public List<Error> Errors { get; set; }
[JsonProperty("warnings")]
public List<Warning> Warnings { get; set; }
}
public partial class Data
{
[JsonProperty("id")]
public Guid Id { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("status")]
public string Status { get; set; }
[JsonProperty("amount")]
public Amount Amount { get; set; }
[JsonProperty("native_amount")]
public Amount NativeAmount { get; set; }
[JsonProperty("description")]
public object Description { get; set; }
[JsonProperty("created_at")]
public DateTimeOffset CreatedAt { get; set; }
[JsonProperty("updated_at")]
public DateTimeOffset UpdatedAt { get; set; }
[JsonProperty("resource")]
public string Resource { get; set; }
[JsonProperty("resource_path")]
public string ResourcePath { get; set; }
[JsonProperty("instant_exchange")]
public bool InstantExchange { get; set; }
[JsonProperty("buy")]
public Buy Buy { get; set; }
[JsonProperty("details")]
public Details Details { get; set; }
[JsonProperty("hide_native_amount")]
public bool HideNativeAmount { get; set; }
}
public partial class Amount
{
[JsonProperty("amount")]
public string AmountAmount { get; set; }
[JsonProperty("currency")]
public string Currency { get; set; }
}
public partial class Buy
{
[JsonProperty("id")]
public Guid Id { get; set; }
[JsonProperty("status")]
public string Status { get; set; }
[JsonProperty("transaction")]
public PaymentMethod Transaction { get; set; }
[JsonProperty("user_reference")]
public string UserReference { get; set; }
[JsonProperty("created_at")]
public DateTimeOffset CreatedAt { get; set; }
[JsonProperty("updated_at")]
public DateTimeOffset UpdatedAt { get; set; }
[JsonProperty("resource")]
public string Resource { get; set; }
[JsonProperty("resource_path")]
public string ResourcePath { get; set; }
[JsonProperty("payment_method")]
public PaymentMethod PaymentMethod { get; set; }
[JsonProperty("committed")]
public bool Committed { get; set; }
[JsonProperty("payout_at")]
public DateTimeOffset PayoutAt { get; set; }
[JsonProperty("instant")]
public bool Instant { get; set; }
[JsonProperty("fee")]
public Amount Fee { get; set; }
[JsonProperty("amount")]
public Amount Amount { get; set; }
[JsonProperty("total")]
public Amount Total { get; set; }
[JsonProperty("subtotal")]
public Amount Subtotal { get; set; }
[JsonProperty("unit_price")]
public UnitPrice UnitPrice { get; set; }
[JsonProperty("hold_until")]
public DateTimeOffset HoldUntil { get; set; }
[JsonProperty("hold_days")]
public long HoldDays { get; set; }
[JsonProperty("idem")]
public Guid Idem { get; set; }
[JsonProperty("next_step")]
public object NextStep { get; set; }
[JsonProperty("is_first_buy")]
public bool IsFirstBuy { get; set; }
[JsonProperty("requires_completion_step")]
public bool RequiresCompletionStep { get; set; }
}
public partial class PaymentMethod
{
[JsonProperty("id")]
public Guid Id { get; set; }
[JsonProperty("resource")]
public string Resource { get; set; }
[JsonProperty("resource_path")]
public string ResourcePath { get; set; }
}
public partial class UnitPrice
{
[JsonProperty("amount")]
public string Amount { get; set; }
[JsonProperty("currency")]
public string Currency { get; set; }
[JsonProperty("scale")]
public long Scale { get; set; }
}
public partial class Details
{
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("subtitle")]
public string Subtitle { get; set; }
[JsonProperty("header")]
public string Header { get; set; }
[JsonProperty("health")]
public string Health { get; set; }
[JsonProperty("payment_method_name")]
public string PaymentMethodName { get; set; }
}
public partial class Error
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("message")]
public string Message { get; set; }
[JsonProperty("url")]
public string Url { get; set; }
}
public partial class Warning
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("message")]
public string Message { get; set; }
[JsonProperty("url")]
public string Url { get; set; }
} The one snag is to deserialize to the proper model when processing a |
Really great find. Thanks for the investigation. I think at a high level; some core questions I have:
My original implementation for Coinbase Trading AKA. GDAX -> Coinbase Pro.... was this library: But now they call it Coinbase Cloud? or Coinbase Advanced? It's like every year they have a new name for this stuff. So I've lost track of what it's officially called now and where the exchange "APIs were moved/migrated to". All this to say:
|
@bchavez First off, thanks for getting back to me. I am like a kid in a candy store, because when I had initially heard about the deprecation I was pretty bummed.
I recall reading somewhere and also here that Coinbase Pro will be/has been sunsetted. I think really all these different "products" are just different routes on the API (i.e., I think they are trying to steer everyone to Advanced Trade. This library will still work, it's just that the following endpoints (your code) no longer work:
Also, placing an order using the For comparison, I am using my home-grown "Coinbase API client" to grab the raw JSON for the same transaction as above, but without the {
"data": {
"id": "fc988949-d761-510b-a862-9bcb45ea85d9",
"type": "buy",
"status": "completed",
"amount": {
"amount": "0.00060521",
"currency": "BTC"
},
"native_amount": {
"amount": "20.00",
"currency": "USD"
},
"description": null,
"created_at": "2023-04-11T19:14:01Z",
"updated_at": "2023-04-13T05:44:31Z",
"resource": "transaction",
"resource_path": "/v2/accounts/67eed12a-ddd2-569e-b262-54a8287fcfb1/transactions/fc988949-d761-510b-a862-9bcb45ea85d9",
"instant_exchange": false,
"buy": {
"id": "b27a7cde-e784-5848-a9dc-a5ca55e94221",
"resource": "buy",
"resource_path": "/v2/accounts/67eed12a-ddd2-569e-b262-54a8287fcfb1/buys/b27a7cde-e784-5848-a9dc-a5ca55e94221"
},
"details": {
"title": "Bought Bitcoin",
"subtitle": "Using NAVY FEDERAL CREDIT UNION ******8602",
"header": "Bought 0.00060521 BTC ($20.00)",
"health": "positive",
"payment_method_name": "NAVY FEDERAL CREDIT UNION ******8602"
},
"hide_native_amount": false
},
"warnings": [
{
"id": "missing_version",
"message": "Please supply API version (YYYY-MM-DD) as CB-VERSION header",
"url": "https://developers.coinbase.com/api#versioning"
}
]
} Listing 1. JSON obtained without using Is Listing 1 what is already modeled in your library? |
@bchavez I would find it highly useful if this library could be adapted to the BTW - FYI - I found that Coinbase Advanced Trade allows you to use "Legacy API Keys" meaning those that work with For institutions, and professional traders, Coinbase has rolled out Coinbase Prime. Coinbase Pro is now called Coinbase Exchange and, oddly enough, my Coinbase Pro API keys still work on the new Coinbase Exchange API, but apparently you are limited to just querying the wallets and order book and such, you cannot place orders anymore on Pro/Exchange. I highly recommend sunsetting your |
I fixed your I also removed your code (and corresponding unit tests) for the I threw together a little dummy console app to test the using Coinbase;
using Coinbase.Models;
namespace DummyTransactionTester
{
public static class Program
{
public static void Main()
{
var xactionsPage = MainImpl()
.GetAwaiter()
.GetResult();
Console.ReadKey();
}
private static async Task<PagedResponse<Transaction>> MainImpl()
{
var config = new ApiKeyConfig { ApiKey = @"*****", ApiSecret = @"****" };
var client = new CoinbaseClient(config);
var xactionsPage = await client.Transactions.ListTransactionsAsync("**** (uuid of a wallet)");
return xactionsPage;
}
}
} Listing 1. Dummy console app. I set a breakpoint at the public partial class Transaction
{
[JsonProperty("buy")]
public Entity Buy { get; set; }
} Listing 2. Old to this: public partial class Transaction
{
[JsonProperty("buy")]
public Buy Buy { get; set; }
[JsonProperty("sell")]
public Sell Sell { get; set; }
public bool ShouldSerializeBuy()
{
return "buy".Equals(Type);
}
public bool ShouldSerializeSell()
{
return "sell".Equals(Type);
}
} Listing 3. New I just used your existing /// <summary>
/// Lists account’s transactions.
/// </summary>
Task<PagedResponse<Transaction>> ITransactionsEndpoint.ListTransactionsAsync(string accountId, PaginationOptions pagination, CancellationToken cancellationToken)
{
return this.AccountsEndpoint
.AppendPathSegmentsRequire(accountId, "transactions")
.SetQueryParam("expand", "all")
.WithPagination(pagination)
.WithClient(this)
.GetJsonAsync<PagedResponse<Transaction>>(cancellationToken);
}
/// <summary>
/// Show an individual transaction for an account. See transaction resource for more information.
/// </summary>
Task<Response<Transaction>> ITransactionsEndpoint.GetTransactionAsync(string accountId, string transactionId, CancellationToken cancellationToken)
{
return this.AccountsEndpoint
.AppendPathSegmentsRequire(accountId, "transactions", transactionId)
.SetQueryParam("expand", "all")
.WithClient(this)
.GetJsonAsync<Response<Transaction>>(cancellationToken);
} Listing 4. New source code for the Notice that, in the new code for the There is no support for placing orders with I will place a PR, and I ask that you consider merging it and then re-publishing to NuGet. Thank you. |
@bchavez I noticed that AppVeyor is having an error and I am trying to fix it. BTW - I also noticed a warning:
I strongly recommend updating |
@bchavez PR #89 is pending an update. I figured I would go ahead and upgrade all the NuGet packages. BTW I am in my Visual Studio and i am seeing the build error:
It applies to the Just a FYI, M$ sunsetted/EOLed .NET Core 2. I propose updating the target to .NET Core 3.1.
Furthermore, I think it would be a good idea to make the I am going to put the updates in place and then make sure it builds before pushing again. Regards, Brian Hart |
At 10:42 hours MDT on 01/04/2024, I am getting a compiler warning:
Listing 1. Let me consult the webpage given and figure out how to do this. .NET Core 2 and 3 have been EOL-ed...so we have to figure out what is the preferred .NET Core version to target (my guess is 7). |
At 10:50 hours MDT on 01/04/2024, I have had a chance to look at https://dotnet.microsoft.com/en-us/platform/support/policy/dotnet-core. It talks about how the various .NET Core versions have a product lifecycle. I strongly advise moving to .NET core 7 (and keeping the other targets). This, I believe will resolve everything and also is in line with what the "cool kids" are programming to these days. I'd move it to .NET Core 6, but the EOL date is 12 November 2024, and I suspect it may be better to go to 7, which is long-term-support LTS (3 years till EOL). I am hoping this will also help with the errors as shown in Figure 2. |
At 11:02 hours MDT on 01/04/2024, so I have 6 compiler errors left, and I've run into a new snag. Apparently, the authors of Working on it... |
RE: Commit 74008b5 Now, to fix the function The current code is: /// <summary>
/// Enable HTTP debugging via Fiddler. Ensure Tools > Fiddler Options... > Connections is enabled and has a port configured.
/// Then, call this method with the following URL format: http://localhost.:PORT where PORT is the port number Fiddler proxy
/// is listening on. (Be sure to include the period after the localhost).
/// </summary>
/// <param name="proxyUrl">The full proxy URL Fiddler proxy is listening on. IE: http://localhost.:8888 - The period after localhost is important to include.</param>
public void EnableFiddlerDebugProxy(string proxyUrl)
{
var webProxy = new WebProxy(proxyUrl, BypassOnLocal: false);
this.Configure(settings =>
{
settings.HttpClientFactory = new DebugProxyFactory(webProxy);
});
} However, the line So I will head on over to the Flurl docs...
So, after perusing the docs, this is how we should implement Message Handlers
Flurl.Http is built on top of HttpClient, which uses HttpClientHandler (by default) for most of its heavy lifting.IFlurlClientBuilder exposes methods for configuring both:
// clientless pattern:
FlurlHttp.Clients.WithDefaults(builder => builder
.ConfigureHttpClient(hc => ...)
.ConfigureInnerHandler(hch => {
hch.Proxy = new WebProxy("https://my-proxy.com");
hch.UseProxy = true;
})); Actually, One thing I think should be done is delete the |
RE: Commit 8a5e53c OK, now I am getting a red error in the /// <summary>
/// Internally used for getting a next or previous page.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="pageUrl"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
protected internal Task<PagedResponse<T>> GetPageAsync<T>(string pageUrl, CancellationToken cancellationToken = default)
{
pageUrl = pageUrl.Remove(0, 4);
return (this.Config.ApiUrl + pageUrl)
.WithClient(this)
.GetJsonAsync<PagedResponse<T>>(cancellationToken);
} The word He says, instead of doing, e.g., "/".WithClient(client) instead do client.Request("/") Aarrghh! Well, I brought this on myself, I suppose, by upgrading So now, e.g., your /// <summary>
/// Internally used for getting a next or previous page.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="pageUrl"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
protected internal Task<PagedResponse<T>> GetPageAsync<T>(string pageUrl, CancellationToken cancellationToken = default)
{
pageUrl = pageUrl.Remove(0, 4);
return (this.Config.ApiUrl + pageUrl)
.WithClient(this)
.GetJsonAsync<PagedResponse<T>>(cancellationToken);
} and it changes to: /// <summary>
/// Internally used for getting a next or previous page.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="pageUrl"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
protected internal Task<PagedResponse<T>> GetPageAsync<T>(string pageUrl, CancellationToken cancellationToken = default)
{
pageUrl = pageUrl.Remove(0, 4);
return this.Request(Config.ApiUrl + pageUrl)
.GetJsonAsync<PagedResponse<T>>(cancellationToken: cancellationToken);
} As you can see, the code just executed a ballet dance, where the Personally, I really have a pet peeve with NuGet package authors changing the external interface upon which other software depends. For this commit, I am proposing to alter all the calls to |
Commit: b7f0c61 For this commit, there are still 70 compiler errors in 14 files. Upon going to the next error, it is complaining about a call a.Should().Throw<ArgumentException>(); in await a.Should().ThrowAsync<ArgumentException>(); After making the change, one of the tests used to be: [Test]
public async Task withdraw
(
[Values(null, "", " ")]string accountId)
{
Func<Task<PagedResponse<Withdrawal>>> a = async () => await client.Withdrawals.ListWithdrawalsAsync(accountId);
Func<Task<Response<Withdrawal>>> b = async () => await client.Withdrawals.WithdrawalFundsAsync(accountId, null);
a.Should().Throw<ArgumentException>();
b.Should().Throw<ArgumentException>();
} and is now: [Test]
public async Task withdraw
(
[Values(null, "", " ")]string accountId)
{
Func<Task<PagedResponse<Withdrawal>>> a = async () => await client.Withdrawals.ListWithdrawalsAsync(accountId);
Func<Task<Response<Withdrawal>>> b = async () => await client.Withdrawals.WithdrawalFundsAsync(accountId, null);
await a.Should().ThrowAsync<ArgumentException>();
await b.Should().ThrowAsync<ArgumentException>();
} |
Commit: 6e0e443 I am getting a new compiler error that says 'Argument 1: cannot convert from 'System.Collections.Generic.IReadOnlyList<Flurl.Http.FlurlCall>' to 'Flurl.Http.Testing.HttpTest' on the following method in // https://github.com/tmenier/Flurl/issues/323
public static HttpCallAssertion ShouldHaveExactCall(this HttpTest test, string exactUrl)
{
test.CallLog[0].Request.Url.ToString().Should().Be(exactUrl);
return new HttpCallAssertion(test.CallLog);
} I propose that this method be deleted, which I've done in the Commit having the SHA referenced above. This is because I read the issue #323 of the Flurl repo and the package author modified the package to take wildcards in |
Commit: 7df8d9a
public static HttpCallAssertion ShouldHaveRequestBody(this HttpTest test, string json)
{
test.CallLog.First().RequestBody.Should().Be(json);
return new HttpCallAssertion(test.CallLog);
} Listing 1. The body of the The compiler does not like the Accoridng to the For example, now the test [Test]
public async Task can_depositfunds()
{
SetupServerSingleResponse(Deposit1);
var create = new DepositFunds
{
Amount = 10.0m,
Currency = "USD",
PaymentMethod = "B28EB04F-BD70-4308-90A1-96065283A001"
};
var r = await client.Deposits.DepositFundsAsync("fff", create );
var truth = new Response<Deposit>
{
Data = Deposit1Model
};
truth.Should().BeEquivalentTo(r);
server.ShouldHaveRequestBody(
@"{""amount"":10.0,""currency"":""USD"",""payment_method"":""B28EB04F-BD70-4308-90A1-96065283A001"",""commit"":false}");
server.ShouldHaveExactCall($"https://api.coinbase.com/v2/accounts/fff/deposits")
.WithVerb(HttpMethod.Post);
} should now look like: [Test]
public async Task can_depositfunds()
{
SetupServerSingleResponse(Deposit1);
var create = new DepositFunds { Amount = 10.0m, Currency = "USD", PaymentMethod = "B28EB04F-BD70-4308-90A1-96065283A001" };
var r = await client.Deposits.DepositFundsAsync("fff", create);
var truth = new Response<Deposit> { Data = Deposit1Model };
truth.Should()
.BeEquivalentTo(r);
server.ShouldHaveCalled("https://api.coinbase.com/v2/accounts/fff/deposits")
.WithRequestBody(@"{""amount"":10.0,""currency"":""USD"",""payment_method"":""B28EB04F-BD70-4308-90A1-96065283A001"",""commit"":false}")
.WithVerb(HttpMethod.Post);
} |
Commit: b8d281d A new compiler error is being thrown on the [Test]
public async Task can_list_payment_methods()
{
SetupServerPagedResponse(PaginationJson, PaymentMethods);
Func<Task<PagedResponse<PaymentMethod>>> action = async () =>
await client.PaymentMethods.ListPaymentMethodsAsync();
action.Should().NotThrow();
} The line [Test]
public async Task can_list_payment_methods()
{
SetupServerPagedResponse(PaginationJson, PaymentMethods);
Func<Task<PagedResponse<PaymentMethod>>> action = async () =>
await client.PaymentMethods.ListPaymentMethodsAsync();
await action.Should().NotThrowAsync();
} |
Commit: 5972e92 In I removed the [OneTimeSetUp]
public void BeforeAllTests()
{
if( !Environment.OSVersion.IsAppVeyor() && Process.GetProcessesByName("Fiddler").Any() )
{
FlurlHttp.Clients.WithDefaults(builder => builder.ConfigureInnerHandler(
hch =>
{
hch.Proxy = new WebProxy("http://localhost.:8888", BypassOnLocal: false);
hch.UseProxy = true;
}
));
}
#if NETFRAMEWORK
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
#endif
} This was needed because the compiler was erroring at the old way. |
Commit: 2187711 Similarly to the previous issue, the compiler was erroring at public IntegrationTests()
{
Directory.SetCurrentDirectory(Path.GetDirectoryName(typeof(IntegrationTests).Assembly.Location));
ReadSecrets();
var webProxy = new WebProxy("http://localhost.:8888", BypassOnLocal: false);
FlurlHttp.Configure(settings =>
{
settings.HttpClientFactory = new ProxyFactory(webProxy);
});
} This is no longer the way it should be carried out; rather, the constructor should look like this: public IntegrationTests()
{
Directory.SetCurrentDirectory(Path.GetDirectoryName(typeof(IntegrationTests).Assembly.Location));
ReadSecrets();
FlurlHttp.Clients.WithDefaults(builder => builder.ConfigureInnerHandler(
hch =>
{
hch.Proxy = new WebProxy("http://localhost.:8888", BypassOnLocal: false);
hch.UseProxy = true;
}
));
} |
Commit: 64df276 Was getting a compiler error in the body of the Originally, the method looks like this: [Test]
public async Task can_hoist_response()
{
bool myCustomActionWasCalled = false;
client.Configure(cf =>
{
cf.AfterCall = http =>
{
myCustomActionWasCalled = true;
"AfterCall action set by user.".Dump();
};
});
var list = await client
.AllowAnyHttpStatus()
.HoistResponse(out var responseGetter)
.Accounts
.ListAccountsAsync();
var response = responseGetter();
response.Should().NotBeNull();
response.StatusCode.Should().NotBe(0);
myCustomActionWasCalled.Should().BeTrue();
} The [Test]
public async Task can_hoist_response()
{
bool myCustomActionWasCalled = false;
client.WithSettings(_ =>
{
client.AfterCall(
call =>
{
myCustomActionWasCalled = true;
"AfterCall action set by user.".Dump();
}
);
});
var list = await client
.AllowAnyHttpStatus()
.HoistResponse(out var responseGetter)
.Accounts
.ListAccountsAsync();
var response = responseGetter();
response.Should().NotBeNull();
response.StatusCode.Should().NotBe(0);
myCustomActionWasCalled.Should().BeTrue();
} |
Commit: 641d9ed Getting compiler errors in |
Commit: c7e278d Next, I had to fix some compiler errors in the Before my change, it was: public class CoinbaseClient
{
/*...*/
/// <summary>
/// Captures the low-level <seealso cref="HttpResponseMessage" /> from a
/// underlying request. Useful in advanced scenarios where you
/// want to check HTTP headers, HTTP status code or
/// inspect the response body manually.
/// </summary>
/// <param name="responseGetter">A function that must be called to
/// retrieve the <seealso cref="HttpResponseMessage"/>
/// </param>
/// <returns>Returns the <seealso cref="HttpResponseMessage"/> of the
/// underlying HTTP request.</returns>
public CoinbaseClient HoistResponse(out Func<IFlurlResponse> responseGetter)
{
IFlurlResponse msg = null;
void CaptureResponse(FlurlCall http)
{
msg = http.Response;
this.Configure(cf =>
{
// Remove Action<HttpCall> from Invocation list
// to avoid memory leak from further calls to the same
// client object.
cf.AfterCall -= CaptureResponse;
});
}
this.Configure(cf =>
{
cf.AfterCall += CaptureResponse;
});
responseGetter = () => msg;
return this;
}
/*...*/
} Now, the method is a lot simpler, i.e.: /// <summary>
/// Captures the low-level <seealso cref="HttpResponseMessage" /> from a
/// underlying request. Useful in advanced scenarios where you
/// want to check HTTP headers, HTTP status code or
/// inspect the response body manually.
/// </summary>
/// <param name="responseGetter">A function that must be called to
/// retrieve the <seealso cref="HttpResponseMessage"/>
/// </param>
/// <returns>Returns the <seealso cref="HttpResponseMessage"/> of the
/// underlying HTTP request.</returns>
public CoinbaseClient HoistResponse(out Func<IFlurlResponse> responseGetter)
{
IFlurlResponse msg = null;
this.WithSettings(_ => this.AfterCall(call => msg = call.Response));
responseGetter = () => msg;
return this;
} |
Commit: d09fca3 Next, there was a compiler error in the method |
OK! I am done. The dependencies are updated and your source code is fixed. See PR #89. In most of my notes above, I've labeled the note with the specific Commit's SHA so you can trace my changes. |
I'm sorry for posting this here as it doesn't belong however I'm unable to find the answer to my question elsewhere. var accounts = await client.Accounts.ListAccountsAsync(); How do I access the data inside of accounts? |
Hi, @c0debase, this is Brian Hart, a contributor. First off, thanks for your question. To use the Any websites linked in this response are purely for educational purposes and are not necessarily endorsed by, nor affiliated with, Brian Chavez or me. I'm simply supplying links that might help you. Here's a section of this repo's Some code that I might utilize is the following -- and be careful when using the code below in your application, as the code below is listed purely for example purposes: // MyClass.cs
//
using Coinbase;
using Coinbase.Models;
namespace MyApp
{
public class MyClass
{
/* ... */
public Task<List<Account>> GetMyAccountsAsync()
{
PaginatedResponse<Account> accounts;
var myAccounts = new List<Account>();
var currentPage = 1;
do
{
accounts = currentPage == 1
? await client.Accounts.ListAccountsAsync(new PaginationOptions { Limit = 100 })
: await client.GetNextPageAsync(accounts);
currentPage++;
if (accounts == null) break; /* something went wrong, stop loop */
if (accounts.Data.Length == 0) break;
myAccounts.AddRange(accounts.Data);
}
while(accounts.HasNextPage());
return myAccounts;
}
/* ... */
}
} Listing 1. Reading the entire list of accounts into a I don't make any express guarantees that the code above works or is fit for a particular purpose; it's purely for example and educational purposes. But it should give you a rough idea of what to do. I just came up with this code on the fly to help you -- it has yet to be tested. The code above is designed to page through all the To talk through the code line by line: The line Next, we set an integer, We make use of a do/while loop to execute our data gathering. We will keep going as long as This keeps track of which page we're retrieving. If As stated previously, we use the ternary conditional operator to help keep our code clean, in order to retrieve the value of the accounts = currentPage == 1
? await client.Accounts.ListAccountsAsync(new PaginationOptions { Limit = 100 })
: await client.GetNextPageAsync(accounts); says: "check the value of the Next, we increment our NOTE: If, instead, you wish to keep track of the number of pages of data obtained, set Next, we check if the If neither of the Keep in mind that the above is just one of many possible different implementations. For clarity, I did not include exception-handling or handling of HTTP errors, or input validation or thread safety/concurrency, which are all things you should think about when writing production code. |
Looks like the advanced trading api is now up and running. Any plans to convert over or make a different api?
The text was updated successfully, but these errors were encountered: