POST requests with the Coinbase Pro API using Google Apps Script

Viewed 505

I'm working with the Coinbase Pro API and while it's working well for GET requests I'm getting an "Invalid signature" when I try a POST request to place an order. I suspect that this may be something related to the "body" of the message since this is the only difference with the GET where "body" is empty.

I'm wondering if someone can help me on this.

function SellMarket (product,amount) {
  var requestPath = '/orders';
  var method = 'POST';
  var body = JSON.stringify({
    "type": "market",
    "side": "buy",
    "product_id": product,
    //"size": amount,
    'size': '1.0'
  });
  
  Logger.log('Sell - Body = '+body); 

  var responseJson = SignAndCallAPI(method, requestPath, body);
  Logger.log('Sell Executed = '+responseJson); 
}  


function SignAndCallAPI(method, requestPath, body) {

  var timestamp = Math.floor(Date.now() / 1000).toString();
  var what = Utilities.base64Decode(Utilities.base64Encode(timestamp + method + requestPath + body));
  var decodedsecret = Utilities.base64Decode(globalvars_CB.secret);
  
  var hmac = Utilities.base64Encode(Utilities.computeHmacSha256Signature(what, decodedsecret));
     
  var options = {
    'method' : method,
    'muteHttpExceptions' : true,     
    'headers' : {
      'Content-Type': 'application/json',
      'CB-ACCESS-KEY' : globalvars_CB.apikey,
      'CB-ACCESS-SIGN' : hmac,
      'CB-ACCESS-TIMESTAMP' : timestamp,
      'CB-ACCESS-PASSPHRASE' :  globalvars_CB.passphrase,
     }
  }  
      
  var responseJson = UrlFetchApp.fetch(globalvars_CB.uri+requestPath, options);
  
  return(responseJson);  

}

1 Answers

Untested suggestion, I recommend changing this bit in your code (in SignAndCallAPI function):

var options = {
  'method' : method,
  'muteHttpExceptions' : true,     
  'headers' : {
    'Content-Type': 'application/json',
    'CB-ACCESS-KEY' : globalvars_CB.apikey,
    'CB-ACCESS-SIGN' : hmac,
    'CB-ACCESS-TIMESTAMP' : timestamp,
    'CB-ACCESS-PASSPHRASE' :  globalvars_CB.passphrase,
  }
}

to:

const options = {
  method: method,
  payload: body,
  contentType: 'application/json',
  muteHttpExceptions: true,     
  headers: {
    'CB-ACCESS-KEY': globalvars_CB.apikey,
    'CB-ACCESS-SIGN': hmac,
    'CB-ACCESS-TIMESTAMP': timestamp,
    'CB-ACCESS-PASSPHRASE':  globalvars_CB.passphrase,
  },
}; 

due to the reasons below:

  1. Although you pass body to SignAndCallAPI function and use body in HMAC computation, you don't appear to actually include body in the POST request sent to the server. I was expecting to see it as the value to a payload property of your options object.

  2. UrlFetchApp documentation (https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app#fetchurl,-params) seems to indicate that Content-Type should be specified in the contentType property of your options object (as opposed to explicitly including a Content-Type header).

  3. I've not looked at the Coinbase API documentation in detail (https://docs.pro.coinbase.com/#place-a-new-order), but body seems okay. You'll also want to make sure the HMAC computation for the CB-ACCESS-SIGN request header is as described in the documentation (https://docs.pro.coinbase.com/#signing-a-message).

Related