Search code examples
javascriptnode.jsencryptiones6-promise

promisified GET call to rest api: can't get a valid signature


I am really having hard time understanding js behavior.

var rp = require('request-promise');
var crypto = require('crypto');



var options = {

  method : 'GET',
  uri : "https://api.binance.com/api/v1/order",

  qs: {
    signature : hash,

    timestamp : Date.now(),
    symbol : 'LTCBTC' 
  },
 headers: {
    'X-MBX-APIKEY' : 'PUBLICKEY'
  },
    json : true
};

var hash= crypto.createHmac('sha256', options.toString())
.update('SECRETKEY')
.digest('hex');

 //console.log(hash);
rp(options)

    .then(function (Bbody) {
        console.log(Bbody);
    })

    .catch(function (err) {
      console.log(err);
    });

if i take the hash function and put it before the options one

it says (obviously)

TypeError: Cannot read property 'toString' of undefined

but if i put it as in the code i shared, i get this error:

StatusCodeError: 400 - {"code":-1102,"msg":"Mandatory parameter 'signature' was not sent, was empty/null, or malformed."}

and this is the output of the request:

 options:                                                                                                                 
{ method: 'GET',                                                                                                          
uri: 'https://api.binance.com/api/v1/order',                                                                            
qs:                                                                                                                      
{ signature: undefined,                                                                                                   
timestamp: 1540392736646,                                                                                               
symbol: 'LTCBTC' },                                                                                                  
headers:                                                                                                                 
{ 'X-MBX-APIKEY': 
'PUBLICKEY' },                                
json: true,                                                                                                             
callback: [Function: RP$callback],                                                                                      
transform: undefined,                                                                                                   
simple: true,                                                                                                           
resolveWithFullResponse: false,                                                                                         
transform2xxOnly: false }, 

If i uncomment the console.log() i get the correct hash printed on video as first output, before the request rejection. Still the call can't catch it.

I have used as docs:

binance api documentation

this node documentation for the crypto library

and this npm doc to promisify api calls

P.S. : PUBLICKEY and SECRETKEY are placeholders but in my tests i used the correct strings.


Solution

  • There's something called variable hoisting in JavaScript. Basically, JavaScript secretly moves the declaration of your hash variable to the beginning of the script as var hash;. When you're constructing the options variable, hash is still undefined.

    Apart from the variable hoisting, I don't think you'll be able to generate a hash of a JavaScript object that should contain the same hash value in it. According to https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#signed-endpoint-examples-for-post-apiv1order, you need to take the following steps:

    1. take all the input parameters for your request (symbol, side, ..., timestamp), and combine them into a query string, for example, symbol=LTCBTC&side=BUY&type=LIMIT&timeInForce=GTC&quantity=1&price=0.1&recvWindow=5000&timestamp=1499827319559
    2. compute HMAC SHA256 signature from the query string
    3. make the request in one of the following two ways:
      • include all your input parameters and the signature as query parameters in the URL (for example: https://api.binance.com/api/v3/order?symbol=LTCBTC&side=BUY&type=LIMIT&...&signature=<your sha256 here>)
      • include all your input parameters and the signature in the body of the request, once again concatenated using &