0

I am attempting to call Gemini API using App Scripts but I am consistently getting the 'InvalidSignature' error message. The code that I am using is a conversion of the python code from their documentation for Private API Invocation.

Code:

function createHeaders(payload) {
  var geminiApiKey = API_KEY
  var geminiApiSecret = SECRET_KEY
  var encodedApiSecret = Utilities.newBlob(geminiApiSecret, 'UTF-8').getBytes()

  var payloadNonce = new Date().getTime() / 1000;
  payload["nonce"] = Math.round(payloadNonce);

  var encodedPayload = Utilities.newBlob(JSON.stringify(payload), 'UTF-8').getBytes()
  var b64 = Utilities.base64Encode(encodedPayload) //string

  var signature = Utilities.computeHmacSignature(
    Utilities.MacAlgorithm.HMAC_SHA_384,
    b64,
    geminiApiSecretAuditor 
  ) //bytes

  return {
    "Content-Type": "text/plain",
    "X-GEMINI-APIKEY": geminiApiKeyAuditor,
    "X-GEMINI-PAYLOAD": b64,
    "X-GEMINI-SIGNATURE": Utilities.base64Encode(signature), //string
    "Cache-Control": "no-cache"
  };
}

function fetchGeminiData() {
  var baseUrl = "https://api.gemini.com";
  var url = baseUrl + "/v1/mytrades";

  var payload = {
    "request": "/v1/mytrades"
  };

  var headers = createHeaders(payload);
  var options = {
    "method": "post",
    "headers": headers,
  };

  var response = UrlFetchApp.fetch(url, options);
  var responseData = response.getContentText();
  var jsonResponse = JSON.parse(responseData);
  Logger.log(jsonResponse);
}

The error messages:

Exception: Request failed for https://api.gemini.com returned code 400. Truncated server response: {"result":"error","reason":"InvalidSignature","message":"InvalidSignature"}

I have ensured that the object types are as per what is expected from each methods, but I still get the above error messages. What am I missing?

Koh
  • 2,687
  • 1
  • 22
  • 62

1 Answers1

0

I believe your goal is as follows.

  • You want to convert the following Python script to Google Apps Script. Ref

    import requests
    import json
    import base64
    import hmac
    import hashlib
    import datetime, time
    
    url = "https://api.gemini.com/v1/mytrades"
    gemini_api_key = "mykey"
    gemini_api_secret = "1234abcd".encode()
    
    t = datetime.datetime.now()
    payload_nonce = time.time()
    payload =  {"request": "/v1/mytrades", "nonce": payload_nonce}
    encoded_payload = json.dumps(payload).encode()
    b64 = base64.b64encode(encoded_payload)
    signature = hmac.new(gemini_api_secret, b64, hashlib.sha384).hexdigest()
    
    request_headers = {
        'Content-Type': "text/plain",
        'Content-Length': "0",
        'X-GEMINI-APIKEY': gemini_api_key,
        'X-GEMINI-PAYLOAD': b64,
        'X-GEMINI-SIGNATURE': signature,
        'Cache-Control': "no-cache"
        }
    
    response = requests.post(url, headers=request_headers)
    
    my_trades = response.json()
    print(my_trades)
    

In this case, how about the following sample script?

Sample script:

function myFunction() {
  API_KEY = "mykey"; // Please set your api key.
  SECRET_KEY = "1234abcd"; // Please set your secret.

  var gemini_api_key = API_KEY;
  var gemini_api_secret = SECRET_KEY;

  var baseUrl = "https://api.gemini.com";
  var url = baseUrl + "/v1/mytrades";
  var nonce = (new Date().getTime() / 1000).toString();
  var payload = `{"request": "/v1/mytrades", "nonce": "${nonce}"}`;
  var b64 = Utilities.base64Encode(payload);
  var signature = Utilities.computeHmacSignature(Utilities.MacAlgorithm.HMAC_SHA_384, b64, gemini_api_secret).map(b => ('0' + (b & 0xFF).toString(16)).slice(-2)).join('');
  var request_headers = {
    'Content-Type': "text/plain",
    'X-GEMINI-APIKEY': gemini_api_key,
    'X-GEMINI-PAYLOAD': b64,
    'X-GEMINI-SIGNATURE': signature,
    'Cache-Control': "no-cache"
  };
  var options = { method: "post", headers: request_headers };
  var response = UrlFetchApp.fetch(url, options);
  var responseData = response.getContentText();
  var jsonResponse = JSON.parse(responseData);
  console.log(jsonResponse);
}

Note:

  • I think that the request of this sample script is the same as the above Python script. But, unfortunately, I cannot test this script. So, if an error occurs, please confirm your API key, your secret, and the URL again.

Reference:

Tanaike
  • 181,128
  • 11
  • 97
  • 165
  • thanks for the code, your code works. But would you be able to explain `Utilities.computeHmacSignature().map(b => ('0' + (b & 0xFF).toString(16)).slice(-2)).join('');`? Why do we need to map? – Koh Aug 20 '23 at 07:46
  • @Koh Thank you for replying. I'm glad your issue was resolved. About `Utilities.computeHmacSignature().map(b => ('0' + (b & 0xFF).toString(16)).slice(-2)).join('');`, `Utilities.computeHmacSignature()` returns the byte array. In your API, I thought that it is required to use the hex string value. So, in order to convert the byte array to the hex string value, I used `map(b => ('0' + (b & 0xFF).toString(16)).slice(-2)).join('')`. If this explanation was not useful, I apologize. – Tanaike Aug 20 '23 at 07:50