I'm trying to use the Coinbase API v2 (NOT PRO) to simply pull the contents of my wallet in to my spreadsheet. I have the following:
var url = 'https://api.coinbase.com/v2';
var requestPath = '/accounts';
var api_key = '********';
var secret_key = '********';
// var timestamp = new Date().getTime().toString().slice(0, -3);
var timestamp = Math.floor(Date.now() / 1000).toString();
var method = 'GET';
var message = Utilities.base64Decode(Utilities.base64Encode(timestamp + method + requestPath));
var decoded_secret = Utilities.base64Decode(secret_key);
var signature = Utilities.base64Encode(Utilities.computeHmacSha256Signature(message, decoded_secret));
//previous attempt
/* var signature = Utilities.computeHmacSha256Signature(message, decoded_secret);
signature = signature.map(function(e) {
var v = (e < 0 ? e + 256 : e).toString(16);
return v.length == 1 ? "0" + v : v;
}).join("");*/
var params = {
'method': method,
'headers': {
'Content-Type': 'application/json',
'CB-ACCESS-SIGN': signature,
'CB-ACCESS-TIMESTAMP': timestamp,
'CB-ACCESS-KEY': api_key,
'CB-VERSION': '2021-02-03'
}
};
Logger.log(params);
var res = await UrlFetchApp.fetch(url + requestPath, params);
Unfortunately I am getting the invalid signature error. I feel like I'm close. It could be that I don't have the url/request path going in to the signature correctly.
Creating a Coinbase signature with Apps Script is a bit tricky
As you can see from the documentation for Coinbase API, in Python the signature is built with
signature = hmac.new(self.secret_key, message, hashlib.sha256).hexdigest().
In other words, the signature needs to be hex-encoded.
There is no 1:1 equivalent to hexdigest() in Apps Script, but you can build it manually.
This works for me:
var message = (timestamp + method + requestPath + body);
var byteSignature = Utilities.computeHmacSha256Signature(message, secret);
var signature = byteSignature.reduce(function(str,chr){
chr = (chr < 0 ? chr + 256 : chr).toString(16);
return str + (chr.length==1?'0':'') + chr;
},'');
Thereby secret is your credential provided by Coinbase - no need to decode it.
Related
I have a public form which incudes a place name, which I'm using the Places API to get the Place ID, to then do another call to look up an address. I need to optimize the volume of API calls so I'm trying to add caching so that Sheets doesn't re-call the two APIs unnecessarily.
For some reason I can't get the caching to work, either it errors or I get "object object" as the response.
function locId(text) {
var text = cache.get(cacheLocId);
if (cacheLocId === null) {
var API_KEY = 'XYZXYZXYZ';
var baseUrl = 'https://maps.googleapis.com/maps/api/place/findplacefromtext/json';
var queryUrl = baseUrl + '?input=' + text + '&inputtype=textquery&key=' + API_KEY;
if (text == null) {
console. log("I QUIT!")
return;
}
var response = UrlFetchApp.fetch(queryUrl);
var json = response.getContentText();
var placeId = JSON.parse(json);
}
cache.put(cacheLocId, placeId)
console. log(text)
console. log(placeId)
return placeId.candidates[0].place_id;
}
It looks like you are overwriting the function argument and using cache keys and values the wrong way around. Try this pattern:
function locId(text) {
if (!text || typeof text !== 'string') {
return null;
}
const cache = CacheService.getDocumentCache();
const cached = cache.get(text);
if (cached) {
return cached;
}
const API_KEY = 'XYZXYZXYZ';
const baseUrl = 'https://maps.googleapis.com/maps/api/place/findplacefromtext/json';
const queryUrl = baseUrl + '?input=' + text + '&inputtype=textquery&key=' + API_KEY;
const response = UrlFetchApp.fetch(queryUrl);
const placeId = JSON.parse(response.getContentText()).candidates[0].place_id;
cache.put(text, placeId);
return placeId;
}
I am trying to do a request to Gate.io cryptocurrency market and getting this error.
{"label":"INVALID_SIGNATURE","message":"Signature mismatch"}
I have managed to find a way to correctly encrypt all the parameters(my output is the same as they presented in their documentation), however, when I add a real secret key I got this error.
Please see the screenshot:
the right-hand side is a signature from their doc, in the GAS log you see my output so these are the same however when I add real timestamp and my real secret then it doesn't work
Screenshot.
Here is the fetch request I am using:
function getDateIo() {
var key = "xxx"
var sec = "xxx"
var timestamp = Math.floor(Date.now() / 1000)
var a = {"contract":"BTC_USD","type":"limit","size":100,"price":6800,"time_in_force":"gtc"}
a = JSON.stringify(a)
var body = {"text":"t-123456","currency_pair":"ETH_BTC","type":"limit","account":"spot","side":"buy","iceberg":"0","amount":"1","price":"5.00032","time_in_force":"gtc","auto_borrow":false}
body = JSON.stringify(body)
console.log(body)
var requestPayload = Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_512, "")
requestPayload = requestPayload.map(function(e) {
var v = (e < 0 ? e + 256 : e).toString(16);
return v.length == 1 ? "0" + v : v;
}).join("");
console.log(requestPayload)
var meth = 'GET'
var pat = '/api/v4/futures/orders'
var sign = meth + "\n" + pat + "\n" + "contract=BTC_USD&status=finished&limit=50" + "\n" + requestPayload + "\n" + timestamp;
console.log(sign)
var signature = Utilities.computeHmacSignature(Utilities.MacAlgorithm.HMAC_SHA_512, sign, sec);
signature = signature.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': {
'Accept': 'application/json',
'Content-Type': 'application/json',
'KEY': key,
'Timestamp': timestamp,
'SIGN': signature,
},
// 'body': body,
'muteHttpExceptions': true,
};
var data = UrlFetchApp.fetch("https://api.gateio.ws/api/v4/futures/orders?contract=BTC_USD&status=finished&limit=50", params);
}
Thanks for any advice
I am creating a Google Sheet add-on wherein I am downloading the FBA Orders. I have been trying to make a successful API call. Am I perhaps missing something here?
Response I am getting:
The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.
Google Sheet JavaScript code:
function POSTRequest() {
var config = amazonMWSConfigProperties.getProperty('amazonConfig');
var configData = JSON.parse(config);
var sellerID=configData.sellerID;
var accessKey=configData.accessKey;
var secretKey=configData.secretKey;
var authToken=configData.authToken;
var defaultMarket=configData.defaultMarket;
var url = 'https://mws.amazonservices.in/Orders/2013-09-01?';
var today1 = new Date();
var today = new Date();
var todayTime = Utilities.formatDate(today, "GMT", "yyyy-MM-dd'T'HH:mm:ss'Z'");
var yesterday = new Date();
yesterday.setDate(today.getDate() - 1);
yesterday.setHours(0,0,0,0);
var yesterdayTime = Utilities.formatDate(yesterday, "GMT", "yyyy-MM-dd'T'HH:mm:ss'Z'");
var dayBeforeYesterday = new Date();
dayBeforeYesterday.setDate(today.getDate() - 2);
dayBeforeYesterday.setHours(0,0,0,0);
var dayBeforeYesterdayTime = Utilities.formatDate(dayBeforeYesterday, "GMT", "yyyy-MM-dd'T'HH:mm:ss'Z'");
var unsignedURL =
'POST\nmws.amazonservices.in\n/Orders/2013-09-01\n'+
'AWSAccessKeyId=' +accessKey+
'&Action=ListOrders'+
'&CreatedAfter='+encodeURIComponent(Utilities.formatDate(new Date(), "GMT", "yyyy-MM-dd'T'HH:mm:ss'Z'")) +
//'&CreatedBefore=2020-01-18T18%3A30%3A00Z' + //yesterdayTime +
'&FulfillmentChannel.Channel.1=AFN' +
'&MWSAuthToken=' +authToken+
'&MarketplaceId.Id.1=' +defaultMarket+
'&SellerId='+sellerID+
'&SignatureMethod=HmacSHA256'+
'&SignatureVersion=2'+
'&Timestamp='+encodeURIComponent(Utilities.formatDate(today1, "GMT", "yyyy-MM-dd'T'HH:mm:ss'Z'")) +
'&Version=2013-09-01';
Logger.log(unsignedURL);
var SignedRequest = calculatedSignature(unsignedURL, secretKey);
var Encoded = Utilities.base64Encode(SignedRequest);
var param = 'AWSAccessKeyId=' +accessKey+
'&Action=ListOrders'+
'&CreatedAfter='+encodeURIComponent(Utilities.formatDate(new Date(), "GMT", "yyyy-MM-dd'T'HH:mm:ss'Z'")) +
//'&CreatedBefore=2020-01-18T18%3A30%3A00Z' + //yesterdayTime +
'&FulfillmentChannel.Channel.1=AFN' +
'&MWSAuthToken=' +authToken+
'&MarketplaceId.Id.1=' +defaultMarket+
'&SellerId='+sellerID+
'&Signature='+encodeURIComponent(SignedRequest) +
'&SignatureMethod=HmacSHA256'+
'&SignatureVersion=2'+
'&Timestamp='+encodeURIComponent(Utilities.formatDate(today1, "GMT", "yyyy-MM-dd'T'HH:mm:ss'Z'")) +
'&Version=2013-09-01';
Logger.log(url+param);
var result = UrlFetchApp.fetch(url+param);
//writeDataToXML(result);
Logger.log(result);
if (result.getResponseCode() == 200) {
//writeDataToXML(result);
}
}
function calculatedSignature(url,secret) {
var urlToSign = url;
var byteSignature = Utilities.computeHmacSha256Signature(urlToSign, secret);
// convert byte array to hex string
var signature = byteSignature.reduce(function(str,chr){
chr = (chr < 0 ? chr + 256 : chr).toString(16);
return str + (chr.length==1?'0':'') + chr;
},'');
//Logger.log("URL to sign: " + urlToSign);
//Logger.log("");
//Logger.log("byte " + byteSignature);
//Logger.log("");
//Logger.log("reg " + signature);
var byte64 = Utilities.base64Encode(byteSignature)
//Logger.log("base64 byte " + Utilities.base64Encode(byteSignature));
//Logger.log("");
//Logger.log("base64 reg " + Utilities.base64Encode(signature));
Logger.log("base64 reg " + byte64)
return byte64;
}
In addition to using the built-in encodeURIComponent, I have found that you also have to manually replace certain values. This is the function that I use to encode my URL:
/**
* Performs URL encoding compatible with MWS http requests
* #param {String} The string to encode
*/
function urlEncode(str) {
str = encodeURIComponent(str);
str = str.replace(/\*/g, '%2A');
str = str.replace(/\(/g, '%28');
str = str.replace(/\)/g, '%29');
str = str.replace(/'/g, '%27');
str = str.replace(/\!/g, '%21');
return str;
}
I call it in my createSignature function:
function createSignature(value, key) {
var signature = Utilities.computeHmacSha256Signature(value, key);
return urlEncode(Utilities.base64Encode(signature));
}
Which I call on my unsigned URL, passing my secret as the key.
I hope that helps!
I'm trying to fetch the API data with Google Apps Script about my balance on Bittrex but it returns me nothing, no errors and only blank cells. This is the code I've written based on the documentation here: https://bittrex.com/Home/Api
function getBalance() {
var ss = SpreadsheetApp.openById("***");
var data = ss.getSheetByName("Data");
var key = ss.getSheetByName("Api").getRange('A2').getValue();
var secret = ss.getSheetByName("Api").getRange('B2').getValue();
var baseUrl = 'https://bittrex.com/api/v1.1/';
var nonce = Math.floor(new Date().getTime()/1000);
var command = "/account/getbalances";
var uri = baseUrl.concat(command + "?apikey=" + key + "&nonce=" + nonce);
var signature = Utilities.computeHmacSignature(Utilities.MacAlgorithm.HMAC_SHA_512,
uri,
secret);
signature = signature.map(function(byte) {
return ('0' + (byte & 0xFF).toString(16)).slice(-2);
}).join('')
var headers = {
"apisign": signature
}
var params = {
"method": "get",
"headers": headers,
}
var response = UrlFetchApp.fetch(uri, params);
var json = JSON.parse(response.getContentText());
var blnc = [];
blnc.push(['Balance']);
for(var key in json.result)
{
blnc[0].push(json.result[key]);
}
askRange = data.getRange(2, 2, blnc.length, 1);
askRange.setValues(blnc);
}
The askrange last number is "1" because the script only pass the "Balance" value to the spreadsheet, but the values should be something around 210. Any help? Thank you
I solved it, the problem was in the "command" variable, there was a slash not needed that generated an url with a double slash
I read solution for MD5 below, but I could not quite get it.
get back a string representation from computeDigest(algorithm, value) byte[]
I'd like to create API signature with HMAC-SHA256 hash.
var date = new Date();
var nonce = Math.floor(date.getTime()/1000);
var url = "https://mysweet.com/api/accounts"
var secret = "my_secret";
var signature = Utilities.computeHmacSha256Signature(nonce+url, secret);
but it returns byte array [42, -8, -47, -21, ..], and it cannot be used as API signature directly.
Is there a simple way to get a Hex value from the method? or conversion?
I applied the method you linked to and get:
var sig = signature.reduce(function(str,chr){
chr = (chr < 0 ? chr + 256 : chr).toString(16);
return str + (chr.length==1?'0':'') + chr;
},'');;
So here's a test function:
function testSig() {
var date = new Date();
var message = "Violets are red";
var secret = "my_secret";
var signature = Utilities.computeHmacSha256Signature(message, secret);
var sig = signature.reduce(function(str,chr){
chr = (chr < 0 ? chr + 256 : chr).toString(16);
return str + (chr.length==1?'0':'') + chr;
},'');
Logger.log(sig); // fe70fa2e74b3ee0d67aa3c1d5c2844e558fea6802e8cfa58e5d4cbdf8bad25fe
//output from http://www.freeformatter.com/hmac-generator.html#ad-output is:
// fe70fa2e74b3ee0d67aa3c1d5c2844e558fea6802e8cfa58e5d4cbdf8bad25fe
}
Plus golf:
Utilities.computeHmacSha256Signature(message, secret)
.map(function(chr){return (chr+256).toString(16).slice(-2)})
.join('')
var date = new Date();
var nonce = Math.floor(date.getTime()/1000);
var url = "https://mysweet.com/api/accounts"
var secret = "my_secret";
var signature = Utilities.computeHmacSha256Signature(nonce+url, secret)
signature = signature.map(function(e) {return ("0" + (e < 0 ? e + 256 : e).toString(16)).slice(-2)}).join("");