FTX API in Google Apps Script not authenticating - cryptography

I've successfully connected to most other crypto exchanges but keep getting "success":false,"error":"Not logged in" when using FTX.
This is my code:
var host = 'https://ftx.com/api';
var endpoint ='/wallet/all_balances';
var url = host + endpoint;
var timestamp = ''+ new Date().getTime();
var payload = timestamp + 'GET' + endpoint+'';
var shaObj = new jsSHA("SHA-256", "BYTES");
shaObj.setHMACKey(secret, "BYTES");
shaObj.update(payload);
var signature = shaObj.getHMAC("HEX");
var options = {
method: 'get',
headers: {
'FTX-KEY': key,
'FTX-TS': timestamp,
'FTX-SIGN': signature
},
muteHTTPExceptions: 'true'
}
var jsondata = UrlFetchApp.fetch(url, options);
var data = JSON.parse(jsondata.getContentText());
Any help much appreciated!

Found the answer:
var host = 'https://ftx.com';
var endpoint ='/api/wallet/all_balances';

Related

can I generate listenKey binance in app script

I try a lot of think to create listenKey in app script but nothing work.
the binance URL: https://binance-docs.github.io/apidocs/spot/en/#user-data-streams
this is my code:
function generated_key() {
var aPIKEY = '*****'; // key.
var secretKey = '****'; // Secret key.
var api = "/api/v3/userDataStream";
var timestamp = Number(new Date().getTime()).toFixed(0);
var string = "timestamp="+timestamp;
var baseUrl = "https://api1.binance.com";
var signature = Utilities.computeHmacSha256Signature(string, secretKey);
signature = signature.map(function(e) {
var v = (e < 0 ? e + 256 : e).toString(16);
return v.length == 1 ? "0" + v : v;
}).join("");
var query = "?" + string + "&signature=" + signature;
var params = {
'method': 'POST',
'headers': {'X-MBX-APIKEY': aPIKEY},
'muteHttpExceptions': true
};
var data = UrlFetchApp.fetch(baseUrl + api + query, params);
var obj = JSON.parse(data); // To JSON
Logger.log(obj)
}
I get the result error: {code=-1101.0, msg=Too many parameters; expected '0' and received '2'.}
any suggestion to do it.

How can I get data from Bitfinex authenticated api endpoints using Google sheets?

Using Google sheets, I have stored my api_key and api_secret in the Property service section of user info as respectively "api_key" and api_secret".
I want to get wallet info from my account. The code I have written is as follows:
function wallet() {
var api_key = PropertiesService.getScriptProperties().getProperty('api_key');
var api_secret = PropertiesService.getScriptProperties().getProperty('api_secret');
var response = UrlFetchApp.fetch("https://api.bitfinex.com/v2/auth/r/wallets", api_key, api_secret);
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("sheet");
var result = JSON.parse(response.getContentText());
var wallet_btc = result.BALANCE
}
When I run in debug mode the error message is:
Cannot find method fetch(string,null,null). (line 13, file "Code")
Is this approach wrong, the code wrong, or both?
Many thanks.
How about the following modifications?
Modification points :
The parameters for UrlFetchApp.fetch() are UrlFetchApp.fetch(url, params). And params is an object.
This is the reason of error Cannot find method fetch(string,null,null). (line 13, file "Code").
When I saw the sample scripts for Bitfinex API, the request body has to be created using api_key, api_secret, nonce, body and signature. And signature is encrypted by HMAC_SHA_384 and converted to the string of the unsigned hexadecimal.
The sample for the endpoint of https://api.bitfinex.com/v2/auth/r/wallets is as follows. This is from API reference.
Sample for the endpoint of https://api.bitfinex.com/v2/auth/r/wallets
request.post(
`${url}/auth/r/wallets`,
headers: { /* auth headers */ },
body: {},
json: true,
(error, response, body) => console.log(body)
)
When above points are reflected to your script, the modified script is as follows.
Modified script :
function wallet() {
var api_key = PropertiesService.getScriptProperties().getProperty('api_key');
var api_secret = PropertiesService.getScriptProperties().getProperty('api_secret');
var apiPath = "v2/auth/r/wallets";
var nonce = Date.now().toString();
var body = {};
var rawBody = JSON.stringify(body);
var signature = "/api/" + apiPath + nonce + rawBody;
signature = Utilities.computeHmacSignature(Utilities.MacAlgorithm.HMAC_SHA_384, signature, api_secret)
.map(function(e) {
var v = (e < 0 ? e + 256 : e).toString(16);
return v.length == 1 ? "0" + v : v;
}).join("");
var url = "https://api.bitfinex.com/" + apiPath;
var options = {
method: 'POST',
contentType: "application/json",
headers: {
'bfx-nonce': nonce,
'bfx-apikey': api_key,
'bfx-signature': signature
},
payload: rawBody
};
var response = UrlFetchApp.fetch(url, options);
var result = JSON.parse(response.getContentText());
Logger.log(result)
// var wallet_btc = result.BALANCE // I couldn't confirm whether this key exists.
}
References :
Sample scripts for Bitfinex API
API reference
UrlFetchApp.fetch()
I cannot confirm whether this works. If this didn't work, can you tell me the situation? I would like to modify.
Edit :
When you want 0.0957596 from the result of [["exchange", "USD", 14.81076629, 0, null], ["exchange", "BTC", 0.0957596, 0, null], ["funding", "BTC", 4.13E-6, 0, null], ["funding", "ETH", 3.50186961, 0, null], ["exchange", "OMG", 5.9E-7, 0, null]];, you can use the following script.
Script :
function wallet() {
var api_key = PropertiesService.getScriptProperties().getProperty('api_key');
var api_secret = PropertiesService.getScriptProperties().getProperty('api_secret');
var apiPath = "v2/auth/r/wallets";
var nonce = Date.now().toString();
var body = {};
var rawBody = JSON.stringify(body);
var signature = "/api/" + apiPath + nonce + rawBody;
signature = Utilities.computeHmacSignature(Utilities.MacAlgorithm.HMAC_SHA_384, signature, api_secret)
.map(function(e) {
var v = (e < 0 ? e + 256 : e).toString(16);
return v.length == 1 ? "0" + v : v;
}).join("");
var url = "https://api.bitfinex.com/" + apiPath;
var options = {
method: 'POST',
contentType: "application/json",
headers: {
'bfx-nonce': nonce,
'bfx-apikey': api_key,
'bfx-signature': signature
},
payload: rawBody
};
var response = UrlFetchApp.fetch(url, options);
var result = JSON.parse(response.getContentText());
// Logger.log(result)
// var wallet_btc = result.BALANCE // I couldn't confirm whether this key exists.
var balance = 0;
for (var i in result) {
if (result[i][0] == "exchange" && result[i][1] == "BTC") {
balance = result[i][2];
break;
}
}
Logger.log(balance)
}
Note :
From the document, it seems that the indexes of WALLET_TYPE, CURRENCY and BALANCE are always 0, 1 and 2 of each element in the response, respectively.

Cryptopia API in Google Sheets (Google Apps Script)

In continuation of building Google SpreadSheet using Google Apps Script I've done with getting my Bittrex and Poloniex balances, but can't get to work with Cryptopia.
Here is a link to my struggles with Bittrex Map JSON objects array to strings
Here is an official API links: https://www.cryptopia.co.nz/Forum/Thread/256
Here are some examples:
https://www.cryptopia.co.nz/Forum/Thread/262
https://github.com/Coac/cryptopia.js/blob/master/index.js
https://github.com/sigwo/node-cryptopia/blob/master/cryptopia.js
Here is my code, which getting "Invalid authorization header" error:
// Get Cryptopia balances
var key = keys.getRange("B4").getValue();
var secret = keys.getRange("C4").getValue();
var baseUrl = 'https://www.cryptopia.co.nz/api/';
var command = "GetBalance";
var url = baseUrl + command;
var signature = key + "POST" + encodeURIComponent(url).toLowerCase() + nonce;
var hmacsignature = Utilities.computeHmacSignature(Utilities.MacAlgorithm.HMAC_SHA_256,signature,secret);
var header_value = "amx " + key + ":" + hmacsignature + ":" + nonce;
var headers = { 'Authorization': header_value, 'Content-Type':'application/json; charset=utf-8' };
var options = {
method: 'POST',
headers: headers
};
var response = UrlFetchApp.fetch("https://www.cryptopia.co.nz/api/GetBalance", options);
var json = JSON.parse(response.getContentText());
}
From your sample links, it seems that hmacsignature is encoded by base64. So how about the following modofication?
From :
var hmacsignature = Utilities.computeHmacSignature(Utilities.MacAlgorithm.HMAC_SHA_256,signature,secret);
To :
var hmacsignature = Utilities.base64Encode(Utilities.computeHmacSignature(Utilities.MacAlgorithm.HMAC_SHA_256,signature,secret));
Note :
Is nonce declared? If you have not declared it, you can use a following script.
var nonce = Math.floor(new Date().getTime() / 1000);
I cannot test this. So I don't know whether this works fine. If this didn't work, I'm sorry.
Edit :
How about this? var params = {}; might be required, even if there is no request parameters. So I added this and Content-Length. And then, is secret encoded by base64? I thought that it may be encoded from other scripts.
var key = keys.getRange("B4").getValue();
var secret = keys.getRange("C4").getValue();
var nonce = Math.floor(new Date().getTime() / 1000); // Added
var params = {}; // Added
var baseUrl = 'https://www.cryptopia.co.nz/api/';
var command = "GetBalance";
var url = baseUrl + command;
var requestContentBase64String = Utilities.base64Encode(Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, JSON.stringify(params), Utilities.Charset.UTF_8)); // Added
var signature = key + "POST" + encodeURIComponent(url).toLowerCase() + nonce + requestContentBase64String; // Modified
var hmacsignature = Utilities.base64Encode(Utilities.computeHmacSignature(Utilities.MacAlgorithm.HMAC_SHA_256, signature, Utilities.base64Decode(secret), Utilities.Charset.UTF_8)); // Modified
var header_value = "amx " + key + ":" + hmacsignature + ":" + nonce;
var headers = {
"Authorization": header_value,
"Content-Type": 'application/json; charset=utf-8',
"Content-Length" : Utilities.newBlob(JSON.stringify(params)).getBytes().length // Added
};
var options = {
method: 'POST',
headers: headers
};
var response = UrlFetchApp.fetch(url, options);
var json = JSON.parse(response.getContentText());
Utilities.computeHmacSignature(algorithm, value, key) method does not compute the binary input correctly. The type of value and key parameter is String, but Utilities.base64Decode's result is Byte[]. The raw binary value is changed while being converted from Byte[] to String.
Use jsSHA, and cf. https://stackoverflow.com/a/14007167.
The following can be used to calculate the correct value, and fetch the result by proper UrlFetchApp.fetch's options.
.... paste src/sha256.js contents ...
...
var params = {"Currency" : "BTC"};
...
var sha = new jsSHA("SHA-256", "TEXT");
sha.setHMACKey(secret, "B64");
sha.update(signature);
var hmacsignature = sha.getHMAC("B64");
var header_value = "amx " + key + ":" + hmacsignature + ":" + nonce;
var headers = {
"Authorization" : header_value,
};
var options = {
"contentType": 'application/json; charset=utf-8',
"method": 'post',
"headers": headers,
"payload": JSON.stringify(params),
"contentLength": JSON.stringify(params).length
};
var response = UrlFetchApp.fetch(url, options);
var json = JSON.parse(response.getContentText());
Logger.log(json);

IdentityServer4 client - Refreshing access tokens on CookieAuthenticationEvents

I am trying to use refresh token when the access token expires. A similar so question is answered here. And a sample code to renew token by an action
And i end up with the following code in the startup.cs
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationScheme = "Cookies",
//ExpireTimeSpan = TimeSpan.FromSeconds(100),
AutomaticAuthenticate = true,
AutomaticChallenge = true,
Events = new CookieAuthenticationEvents()
{
OnValidatePrincipal = async x =>
{
if (x.Properties?.Items[".Token.expires_at"] == null) return;
var logger = loggerFactory.CreateLogger(this.GetType());
var now = DateTimeOffset.UtcNow;
var tokenExpireTime = DateTime.Parse(x.Properties.Items[".Token.expires_at"]).ToUniversalTime();
var timeElapsed = now.Subtract(x.Properties.IssuedUtc.Value);
var timeRemaining = tokenExpireTime.Subtract(now.DateTime);
if (timeElapsed > timeRemaining)
{
var httpContextAuthentication = x.HttpContext.Authentication;//Donot use the HttpContext.Authentication to retrieve anything, this cause recursive call to this event
var oldAccessToken = await httpContextAuthentication.GetTokenAsync("access_token");
var oldRefreshToken = await httpContextAuthentication.GetTokenAsync("refresh_token");
logger.LogInformation($"Refresh token :{oldRefreshToken}, old access token:{oldAccessToken}");
var disco = await DiscoveryClient.GetAsync(AuthorityServer);
if (disco.IsError) throw new Exception(disco.Error);
var tokenClient = new TokenClient(disco.TokenEndpoint, ApplicationId, "secret");
var tokenResult = await tokenClient.RequestRefreshTokenAsync(oldRefreshToken);
logger.LogInformation("Refresh token requested. " + tokenResult.ErrorDescription);
if (!tokenResult.IsError)
{
var oldIdToken = await httpContextAuthentication.GetTokenAsync("id_token");
var newAccessToken = tokenResult.AccessToken;
var newRefreshToken = tokenResult.RefreshToken;
var tokens = new List<AuthenticationToken>
{
new AuthenticationToken {Name = OpenIdConnectParameterNames.IdToken, Value = oldIdToken},
new AuthenticationToken {Name = OpenIdConnectParameterNames.AccessToken, Value = newAccessToken},
new AuthenticationToken {Name = OpenIdConnectParameterNames.RefreshToken, Value = newRefreshToken}
};
var expiresAt = DateTime.UtcNow + TimeSpan.FromSeconds(tokenResult.ExpiresIn);
tokens.Add(new AuthenticationToken { Name = "expires_at", Value = expiresAt.ToString("o", CultureInfo.InvariantCulture) });
var info = await httpContextAuthentication.GetAuthenticateInfoAsync("Cookies");
info.Properties.StoreTokens(tokens);
await httpContextAuthentication.SignInAsync("Cookies", info.Principal, info.Properties);
}
x.ShouldRenew = true;
}
else
{
logger.LogInformation("Not expired");
}
}
}
});
The client setup is as follows
AllowAccessTokensViaBrowser = true,
RefreshTokenUsage = TokenUsage.ReUse,
RefreshTokenExpiration = TokenExpiration.Sliding,
AbsoluteRefreshTokenLifetime = 86400,
AccessTokenLifetime = 10,
AllowOfflineAccess = true,
AccessTokenType = AccessTokenType.Reference
After successfully login, i am getting a 401 for every other request. And the log says
[Identity Server]2017-07-04 10:15:58.819 +01:00 [Debug]
"TjpIkvHQi../cfivu6Nql5ADJJlZRuoJV1QI=" found in database: True
[Identity Server]2017-07-04 10:15:58.820 +01:00 [Debug]
"reference_token" grant with value:
"..9e64c1235c6675fcef617914911846fecd72f7b372" found in store, but has
expired.
[Identity Server]2017-07-04 10:15:58.821 +01:00 [Error] Invalid
reference token. "{ \"ValidateLifetime\": true,
\"AccessTokenType\": \"Reference\", \"TokenHandle\":
\"..9e64c1235c6675fcef617914911846fecd72f7b372\" }"
[Identity Server]2017-07-04 10:15:58.822 +01:00 [Debug] Token is
invalid.
[Identity Server]2017-07-04 10:15:58.822 +01:00 [Debug] Creating
introspection response for inactive token.
[Identity Server]2017-07-04 10:15:58.822 +01:00 [Information] Success
token introspection. Token status: "inactive", for API name: "api1"
Any help would by highly appreciated
UPDATE:
Basically, when the token expires i get a System.StackOverflowException on the following line
var tokenExpireTime = DateTime.Parse(x.Properties.Items[".Token.expires_at"]).ToUniversalTime();
UPDATE 2:
Do not use HttpContext.Authentication to retrieve anything. Check my answer below to find the working implementaion
I was working on this for last two days and could not make this work. Funnily, after posting the question here, within 2 hours I make it working :)
Events = new CookieAuthenticationEvents()
{
OnValidatePrincipal = async x =>
{
if (x.Properties?.Items[".Token.expires_at"] == null) return;
var now = DateTimeOffset.UtcNow;
var tokenExpireTime = DateTime.Parse(x.Properties.Items[".Token.expires_at"]).ToUniversalTime();
var timeElapsed = now.Subtract(x.Properties.IssuedUtc.Value);
var timeRemaining = tokenExpireTime.Subtract(now.DateTime);
WriteMessage($"{timeRemaining} and elapsed at {timeElapsed}");
if (timeElapsed > timeRemaining)
{
var oldAccessToken = x.Properties.Items[".Token.access_token"];
var oldRefreshToken = x.Properties.Items[".Token.refresh_token"];
WriteMessage($"Refresh token :{oldRefreshToken}, old access token {oldAccessToken}");
var disco = await DiscoveryClient.GetAsync(AuthorityServer);
if (disco.IsError) throw new Exception(disco.Error);
var tokenClient = new TokenClient(disco.TokenEndpoint, ApplicationId, "secret");
var tokenResult = await tokenClient.RequestRefreshTokenAsync(oldRefreshToken);
if (!tokenResult.IsError)
{
var oldIdToken = x.Properties.Items[".Token.id_token"];//tokenResult.IdentityToken
var newAccessToken = tokenResult.AccessToken;
var newRefreshToken = tokenResult.RefreshToken;
var tokens = new List<AuthenticationToken>
{
new AuthenticationToken {Name = OpenIdConnectParameterNames.IdToken, Value = oldIdToken},
new AuthenticationToken {Name = OpenIdConnectParameterNames.AccessToken, Value = newAccessToken},
new AuthenticationToken {Name = OpenIdConnectParameterNames.RefreshToken, Value = newRefreshToken}
};
var expiresAt = DateTime.UtcNow + TimeSpan.FromSeconds(tokenResult.ExpiresIn);
tokens.Add(new AuthenticationToken { Name = "expires_at", Value = expiresAt.ToString("o", CultureInfo.InvariantCulture) });
x.Properties.StoreTokens(tokens);
WriteMessage($"oldAccessToken: {oldAccessToken}{Environment.NewLine} and new access token {newAccessToken}");
}
x.ShouldRenew = true;
}
}
}
Basically httpContextAuthentication.GetTokenAsync make this recursive, for that reason StackOverflowException occured.
Please let me know if this implementation has any issue

Shopify authentication using the google app script Class OAuthConfig

I am trying to connect with my shopify shop through the google javascrip. The schema for authentication should be something similar to the one you can find on google documentation for twitter. I'am trying the following code, but I always get the error:{"errors":"[API] Invalid API key or access token (unrecognized login or wrong password)"}
function getInfofromshopify() {
var handle = "01-02-0316_cmt_utensili"
var urljson ="https://mysitename.myshopify.com/admin/products.json?handle="+handle;
var oAuthConfig = UrlFetchApp.addOAuthService("shopify");
oAuthConfig.setAccessTokenUrl("https://mysitename.myshopify.com/admin/oauth/access_token");
oAuthConfig.setRequestTokenUrl("https://mysitename.myshopify.com/admin/oauth/access_token");
oAuthConfig.setAuthorizationUrl("https://mysitename.myshopify.com/admin/oauth/authorize");
oAuthConfig.setConsumerKey(API_KEY);
oAuthConfig.setConsumerSecret(Shared_secret);
var options =
{
"oAuthServiceName" : "shopify",
"oAuthUseToken" : "always"
};
var response = UrlFetchApp.fetch(urljson,options);
var responsestr = response.getContentText();
var result = Utilities.jsonParse(responsestr)
}
This worked for me:
var url = "https://<YOUR_SHOP>.myshopify.com/admin/products.json";
var username = "<YOUR_SHOPIFY_API_KEY>";
var password = "<YOUR_SHOPIFY_API_PASSWORD>";
var response = UrlFetchApp.fetch(url, {"method":"get", "headers": {"Authorization": "Basic " + Utilities.base64Encode(username + ":" + password)}});