2

So I'm trying to test an authenticated response from Gemini. I get a "response", but not what I expect... it comes back as an html string, not "an array of balances" like the API suggests I should get. I haven't used HttpClient before, so double learning curve here. Not sure if I have a bad message, or if I'm looking at the wrong method/property?

I'm a bit stuck as to how to get this array returned from Gemini.

I have the API Key and Secret there as this is just on the sandbox. Account has ~1,000,000 fake assets.

JSON String

{"request":"/v1/balances","nonce":1477275934999}

Base64 Encoding of JSON / Payload

eyJyZXF1ZXN0IjoiL3YxL2JhbGFuY2VzIiwibm9uY2UiOjE0NzcyNzU5MzQ5OTl9

Post Message

Method: POST, RequestUri: 'https://api.sandbox.gemini.com/', Version: 1.1, Content: System.Net.Http.StringContent, Headers:
{
  X-GEMINI-APIKEY: L6qDKmQZcmLVDTd5zK9S
  X-GEMINI-PAYLOAD: eyJyZXF1ZXN0IjoiL3YxL2JhbGFuY2VzIiwibm9uY2UiOjE0NzcyNzU5MzQ5OTl9
  X-GEMINI-SIGNATURE: DBA15C6B2FFA602F9323E0180A14BABEA018640324E313D6C8BBAE9F8872B06325B75F6C507C55435982D600EE5DDF57
  Content-Type: text/plain; charset=utf-8
}

Response string: (always seems to 'work', even when I had mal-formed requests I didn't notice for a bit)

StatusCode: 200, ReasonPhrase: 'OK', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
  Connection: keep-alive
  Age: 6605
  X-Cache: Hit from cloudfront
  X-Amz-Cf-Id: Ae3ps4mhbjIzGCrgFr43dq0ICZvnUmPKpeo1miv4ufDFJkJtNZy8GA==
  Date: Sat, 22 Oct 2016 22:55:51 GMT
  ETag: "5dc6bbdccdd2100c4ca4b0155402db35"
  Server: AmazonS3
  Via: 1.1 cae81d5ff1d682b28f2deabdd94777d4.cloudfront.net (CloudFront)
  Content-Length: 172
  Content-Type: text/html
  Last-Modified: Fri, 29 Jul 2016 18:31:06 GMT
}

Response.Content - Not what I expected...

<html>
    <head>
        <script>
            window.location.replace("/rest-api/" + window.location.search + window.location.hash);
        </script>
    </head>
</html>

Code (C# .NET 4.5)

using System;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Gemini.API;
using Newtonsoft.Json;

namespace HodlBot
{
    class Program
    {
        public const int TIME_INTERVAL_IN_MILLISECONDS = 3000;
        private static Timer m_timer = null;

        private static void Main(string[] args)
        {
            var source = new CancellationTokenSource();
            Console.CancelKeyPress += (s, e) =>
            {
                e.Cancel = true;
                source.Cancel();
            };

            m_timer = new System.Threading.Timer(Tick, source, TIME_INTERVAL_IN_MILLISECONDS, Timeout.Infinite);

            source.Token.WaitHandle.WaitOne();
        }

        private async static void Tick(object p_state)
        {
            Console.Clear();
            var c = (CancellationTokenSource)p_state;

            /* All requests must contain a nonce, a number that will never be repeated and must increase between requests. This is to prevent an attacker who has
             * captured a previous request from simply replaying that request. We recommend using a millisecond-level timestamp. The nonce need only be
             * increasing with respect to the session that the message is on.*/

            long nonce = DateTimeOffset.Now.ToUnixTimeMilliseconds();
            string jObject = JsonConvert.SerializeObject(new Balance(nonce));

            await POST(jObject);

            // Re-Prime Event
            if(!c.Token.IsCancellationRequested)
                m_timer.Change(TIME_INTERVAL_IN_MILLISECONDS, Timeout.Infinite);
        }

        private static async Task POST(string jsonText)
        {
            // Sign Payload
            Console.WriteLine(jsonText);
            string payload = System.Convert.ToBase64String((Encoding.UTF8.GetBytes(jsonText)));

            Console.WriteLine(payload);

            HMACSHA384 hmac = new HMACSHA384(Encoding.UTF8.GetBytes("LixWGpx3h89D8B4uQvfxd8hy5N1"));           //My API SECRET
            byte[] hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(payload));
            string hexHash = BitConverter.ToString(hash).Replace("-", "");

            using(var httpClient = new HttpClient())
            using(var stringContent = new StringContent("", Encoding.UTF8, "text/plain"))
            using(var r = new HttpRequestMessage(HttpMethod.Post, "https://api.sandbox.gemini.com"))
            {
                r.Headers.Add("X-GEMINI-APIKEY", "L6qDKmQZcmLVDTd5zK9S");
                r.Headers.Add("X-GEMINI-PAYLOAD", payload);
                r.Headers.Add("X-GEMINI-SIGNATURE", hexHash);
                r.Content = stringContent;

                Console.WriteLine(r.ToString());

                var response = await httpClient.SendAsync(r);
                if(response.IsSuccessStatusCode)
                {
                    Console.WriteLine(response.ToString());
                    var content = await response.Content.ReadAsStringAsync();
                    Console.WriteLine(content);
                }
            }
        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;

namespace Gemini.API
{
    public class BasicRequest
    {
        [JsonProperty("request", Order = 1)]
        public string Request { get; internal set; }

        [JsonProperty("nonce", Order = 2)]
        public long Nonce { get; internal set; }

        public BasicRequest(string request, long nonce)
        {
            Request = request;
            Nonce = nonce;
        }
    }

    public class HeartBeat : BasicRequest
    {
        public HeartBeat(long nonce) : base("/v1/heartbeat", nonce) { }
    }


    public class Balance : BasicRequest
    {
        public Balance(long nonce) : base("/v1/balances", nonce) { }
    }
}
2

In addition to setting the request parameter in the payload, you'll need to make the POST request to the correct URI as well.

So where you wrote:

using(var r = new HttpRequestMessage(HttpMethod.Post, "https://api.sandbox.gemini.com"))
{
    ...
}

you'll need something like:

var request = "/v1/balances"; // or whatever endpoint you're trying to use
var url = "https://api.sandbox.gemini.com" + request;
using(var r = new HttpRequestMessage(HttpMethod.Post, url))
{
    ...
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.