I am trying to access the GDAX API private methods which can be seen here, GDAX Documentation
There is some great Python code that does it that I am trying to recreate in Mathematica. That code can be seen here and below.
import base64, hashlib, hmac, time
timestamp = str(time.time())
message = timestamp + request.method + request.path_url + (request.body or '')
hmac_key = base64.b64decode(secret_key)
signature = hmac.new(hmac_key, message.encode('utf-8'), hashlib.sha256)
signature_b64 = base64.b64encode(signature.digest())
headers = {
'CB-ACCESS-SIGN': signature_b64,
'CB-ACCESS-TIMESTAMP': timestamp,
'CB-ACCESS-KEY': api_key,
'CB-ACCESS-PASSPHRASE': passphrase,
'Content-Type': 'application/json'
}
# add in headers when we create a request
requests.post(order_url, data=data, headers=headers)
Here is what I have so far:
gdaxGetBalancesF[APIKey_, APISecret_, passphrase_] := Module[
{URL1, nonce, response, body},
URL1 = URLBuild[{"https://api.gdax.com", "accounts"}];
nonce = ToString[UnixTime[]];
body = "";
response =
gdaxPayloadResponseF[URL1, "GET", nonce, body, APIKey, APISecret,passphrase]
]
gdaxPayloadResponseF[url_, type_, nonce_, body_, APIKey_, APISecret_, passphrase_] := Module[
{query, message, gdaxAPISecret, request, response},
query = URLParse[url];
message = nonce <> type <> URLBuild[query[[{"Path"}]]] <> body;
(*decode APISecret*)
gdaxAPISecret = ImportString[APISecret, {"Base64", "String"}];
request =
HTTPRequest[
URLBuild[query[[{"Scheme", "Domain", "Path"}]]],
<|
Method -> type,
"ContentType" -> "application/json",
"Body" -> body,
"Headers" ->
<|
"CB-ACCESS-KEY" -> APIKey,
"CB-ACCESS-SIGN" -> fixBase64[ExportString[hmacF["SHA256", message,gdaxAPISecret,{"Base64","String"}]],
"CB-ACCESS-TIMESTAMP" -> nonce,
"CB-ACCESS-PASSPHRASE" -> passphrase
|>
|>
];
response = URLExcute[request];
]
fixBase64[string_] := StringTrim[StringReplace[string, {"\n"} -> ""]]
hmacF[method_, message_, key_] := Module[
{keyLen, dkey, opad, ipad, blocksize},
blocksize =If[method === "SHA384" || method === "SHA512",128,64];
keyLen = StringLength[key];
dkey = If[keyLen > blocksize,
IntegerString[Hash[key, method], 16],
StringPadRight[key, blocksize, FromCharacterCode[0]]
];
{opad, ipad} = FromCharacterCode[BitXor[ToCharacterCode@StringRepeat[FromCharacterCode[FromDigits[#, 16]], blocksize],ToCharacterCode@dkey]] & /@ {"5c", "36"};
IntegerString[Hash[StringJoin[opad,ExportString[IntegerDigits[Hash[StringJoin[ipad, message], method], 256],"Binary"]],method],16]
]
I always get the signing error invalid signature.
<|StatusCode->400,Body->{"message":"invalid signature"}|>
I think the errors are in two places that I can't seem to figure out. First is decoding the APISecret, see here and then getting that to work with a HMAC mathematica implementation. So far this hmac function has worked on all my requests that need to be signed but none of those request required base64 encoding and decoding. It looks like the python implementation works on byte arrays while this current MMA implementation uses strings. I am really stuck and would like to avoid doing this in JLink/Java.
ExportString[hmacF["SHA256", message,gdaxAPISecret],{"Base64","String"}]]
andURLExecute
. Also, you might try usingDeveloper
EncodeBase64` instead of ExportString. It's difficult to reproduce the issue without having a GDAX account. Comparing the output offixBase64[...]
with that from python might be useful. $\endgroup$