apigoogle-apps-scriptsignaturebitmex

{error={name=HTTPError, message=Signature not valid.}} Bitmex Api connection error


I've been trying to the best of my capability to interprete bitmex's indications for api connection, but this is all I get:

function balanceBitmex() {

  var apiKey = '***';
  var apiSecret = '***';

  var verb = 'get';
  var path = '/api/v1/user/wallet';

  var expires = Number(new Date().getTime() +5).toFixed(0);
  var expires = expires.toString();


  var data = '';
  var data = JSON.stringify(data);
  var currency = '';
  var count= '';
  var concat = verb+path+expires+data;


  var apiSignature = Utilities.computeHmacSha256Signature(apiSecret, concat);
  apiSignature = apiSignature.map(function(e) {
      var v = (e < 0 ? e + 256 : e).toString(16);
      return v.length == 1 ? "0" + v : v;
  }).join("");


  var params = {
    'method': 'get',
    'headers': {'contentType': 'application/json',
                'api-expires': expires,
                'api-key': apiKey,
                'api-signature': apiSignature},
    'muteHttpExceptions': true
  };

  //var params = params + data;

  var url = "https://www.bitmex.com/api/v1/user/wallet?currency=XBt";

  var data = UrlFetchApp.fetch(url, params);
  var data = JSON.parse(data.getContentText());

what is wrong with my script? bitmex api explorer - user wallet


Solution

  • The function Utilities.computeHmacSha256Signature(String, String) takes two parameters, in the following order:

    1. value: The input value to generate a hash for.
    2. key: A key to use to generate the hash with.

    In your case, you are essentially encrypting the apiSecret using concat as the key. However, it should be the opposite - the apiSecret should be the key used to encrypt concat. You simply have to change:

    var apiSignature = Utilities.computeHmacSha256Signature(apiSecret, concat);
    

    For the following:

    var apiSignature = Utilities.computeHmacSha256Signature(concat, apiSecret);
    

    Furthermore, you can use the following code to calculate the example provided in the BitMex documentation and verify it properly works.

    function testEncryption() {
      var apiSecret = 'chNOOS4KvNXR_Xq4k4c9qsfoKWvnDecLATCRlcBwyKDYnWgO';
      var verb = 'GET';
      var path = '/api/v1/instrument';
      var expires = 1518064236; 
      var data = '';
    
      var apiSignature = Utilities.computeHmacSha256Signature(verb + path + expires + data, apiSecret);
      apiSignature = apiSignature.map(function(e) {
          var v = (e < 0 ? e + 256 : e).toString(16);
          return v.length == 1 ? "0" + v : v;
      }).join("");
    
      Logger.log(apiSignature);
    }
    

    The result of running the code above is c7682d435d0cfe87c16098df34ef2eb5a549d4c5a3c2b1f0f77b8af73423bf00, which is the same result shown in the BitMex documentation.

    Regarding the code you provided, there are a few more mistakes that have to be addressed in order to make the API work:

    1. verb parameter: The parameter you specified is get, but the API only accepts this parameter as uppercase (GET). Using the former, the signature will be different thus making the request fail.
    2. path querystring: The querystring (in the case of your request currency=XBt) has to be present when computing the signature, but in your code it is not (notice the line var path = '/api/v1/user/wallet';)

    Your final code, after fixing all the errors above and cleaning up some unused variables, could look like below:

    function balanceBitmex() {
      var apiKey = '***'
      var apiSecret = '***';
    
      var verb = 'GET';
    
      var path = '/api/v1/user/wallet?currency=XBt';
    
      var expires = Math.floor((Date.now() / 1000) + 600).toFixed(0);
    
      var concat = verb + path + expires;
    
      var apiSignature = Utilities.computeHmacSha256Signature(concat, apiSecret);
      apiSignature = apiSignature.map(function(e) {
          var v = (e < 0 ? e + 256 : e).toString(16);
          return v.length == 1 ? "0" + v : v;
      }).join("");
    
      var params = {
        'headers': {
          'api-expires': expires,
          'api-key': apiKey,
          'api-signature': apiSignature
        },
        'muteHttpExceptions': true
      };
    
      var url = "https://www.bitmex.com/api/v1/user/wallet?currency=XBt";
    
      var response = UrlFetchApp.fetch(url, params);
      var data = JSON.parse(response.getContentText());
    
      Logger.log(data);
    }