Market Order-Appropriate inputs and is Body the same as Payload?

Hi, I am trying to make a Market Order to buy bitcoin and have two questions. The response I get is “Invalid signature” . I’ve been able to use the python CB signature code provided in the forum for other requests with no problem (accounts, ticker, ledger, Oracle) and have two questions.

The API documentation provided the following for Market Order for BTC-USD"
payload = {
“profile_id”: “default profile_id”,
“type”: “market”,
“side”: “buy”,
“stp”: “dc”,
“stop”: “loss”,
“time_in_force”: “GTC”,
“cancel_after”: “min”,
“post_only”: “false”,
“product_id”: “BTC-USD”,
“size”: “.00063”
}
headers = {
“Accept”: “application/json”,
“Content-Type”: “application/json”,
‘CB-ACCESS-KEY’: cb_api_key,
‘CB-ACCESS-PASSPHRASE’: cb_access_passphrase,
‘CB-ACCESS-SIGN’: signature,
‘CB-ACCESS-TIMESTAMP’: cb_access_timestamp
}

response = requests.request(“POST”, url, json=payload, headers=headers)

print(response.text)

First, I’ve tried the above code without success and also the following payload with only the required Market Order fields, but still get “Invalid signature .” Is the API expecting something else for Market Orders?
payload = {
“type”: “market”,
“side”: “buy”,
“post_only”: “false”,
“product_id”: “BTC-USD”,
“size”: “.00063”
}

Secondly, the API documentation provided:
“response = requests.request(“POST”, url, json=payload, headers=headers)”

Does this mean the payload is separate from the HMAC authorization signature? Is the payload included twice (HMAC signature and then again as json payload?) I have been passing the payload as the body for the signature as shown in the following code:

cb_access_timestamp = str(time.time())

cb_access_passphrase = ‘’
cb_api_key = ‘’
secret = ‘’

path = ‘/orders’
body = {
“type”: “market”,
“side”: “buy”,
“post_only”: “false”,
“product_id”: “BTC-USD”,
“size”: “.00063”
}
method = ‘POST’
message = ‘{}{}{}{}’.format(cb_access_timestamp, method, path, body)
url = ‘https://api-public.sandbox.exchange.coinbase.com{}’.format(path)

hmac_key = base64.b64decode(secret)
digest = hmac.new(hmac_key, message.encode(‘utf-8’), digestmod=hashlib.sha256).digest()
signature = base64.b64encode(digest).decode(‘utf-8’)

headers = {
‘Content-Type’: ‘application/json’,
‘CB-ACCESS-KEY’: cb_api_key,
‘CB-ACCESS-PASSPHRASE’: cb_access_passphrase,
‘CB-ACCESS-SIGN’: signature,
‘CB-ACCESS-TIMESTAMP’: cb_access_timestamp
}

response = requests.post(url, headers=headers)
print(response.json())

Since the error is “Invalid signature” for various attempts with the payload in and not in the signature, and passed both in the signature and separately as JSON data, any clarification would be much appreciated.

I’ve also read that some APIs would reject the response if trailing zeroes where not included.

For BTC_USD the specific parameters are : {’
id’: ‘BTC-USD’,
‘base_currency’: ‘BTC’,
‘quote_currency’: ‘USD’,
‘base_min_size’: ‘0.000016’,
‘base_max_size’: ‘230’,
‘quote_increment’: ‘0.01’,
‘base_increment’: ‘0.00000001’,
‘display_name’: ‘BTC/USD’,
‘min_market_funds’: ‘1’,
‘max_market_funds’: ‘15000000’,
‘margin_enabled’: False,
‘fx_stablecoin’: False,
‘max_slippage_percentage’: ‘0.02000000’,
‘post_only’: False,
‘limit_only’: False,
‘cancel_only’: False,
‘trading_disabled’: False,
‘status’: ‘online’,
‘status_message’: ‘’,
‘auction_mode’: False},

since the Base increment is “0.00000001” I’ve also tried the size as “0.00063000” without success

1 Like

Thank you for the details regarding your issue and question @SubStandard - Checking on this for you with our team to see how we can best assist. Stay tuned…

2 Likes

thank you for looking into it! @arood

Just a shot in the dark but it looks like body is an array when you send it to the prehash. Should be a string. If you where attempting to use authentication on an endpoint that doesn’t require it then the api would have ignored the authentication protocol on failure. I went thru that one myself.

1 Like

@cleggink Thanks for the recommendation. I’ll give it a try!

I passed it as:

body = str({“type”: “market”, “side”: “buy”, “time_in_force”: “GTC”, “post_only”: “false”, “product_id”: “BTC-USD”, “size”: “0.00063000”})

but still invalid signature. I tried using the " " to make it a string as well but got syntax errors. I was just reading on the Crypto.Com API that for them, they need to put parameters into alphabetical order before passing it through the hash. I might try that next.

Your getting syntax errors because of the literals. Your quotes need to be backslash " not just ". I can’t put the backslash no matter how many I type… no spaces either

Your http request should look similar to this:

HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(String.join("", coinbaseProBaseURL, endpoint)))
.header(“CB-ACCESS-KEY”, coinbaseSecretKey)
.header(“CB-ACCESS-PASSPHRASE”, coinbasePassphrase)
.header(“CB-ACCESS-SIGN”, signMessage(timeStamp, “POST”, endpoint, restRequestBody))
.header(“CB-ACCESS-TIMESTAMP”, timeStamp).header(“Accept”, “application/json”)
.header(“Content-Type”, “application/json”)
.header(“Accept”, “application/json”)
.method(“POST”, HttpRequest.BodyPublishers.ofString(restRequestBody))
.build();

and my signature encryption function:

private String signMessage(String timestamp, String method, String path, String body) // ENCRYPTED HASH SECURITY SIGNATURE BUILDER
		throws NoSuchAlgorithmException, InvalidKeyException {

	String prehash = timestamp + method + path + body;
	Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
	byte[] secretDecoded = Base64.getDecoder().decode(this.coinbaseAPIKey);
	SecretKeySpec secret_key = new SecretKeySpec(secretDecoded, "HmacSHA256");
	sha256_HMAC.init(secret_key);
	return Base64.getEncoder().encodeToString(sha256_HMAC.doFinal(prehash.getBytes()));
}

Hope this helps

And now that I look at it again I think you have your secret key and api key swapped, api key goes in the signature encryption.

Thank you @cleggink !! This appears to be correct @SubStandard - Loving the collaboration. Please let us know if you continue to have issues.

2 Likes

No problem. Glad to help :sunglasses:

Thanks @cleggink and @arood! I’ll give it a go and hopefully it will work, but this does open up another question as I was using the code provided at “Coinbase Pro API Authentication - #14 by larry.kubin” which works for the Oracle, accounts, and ledger signing which uses the API secret in the same spot I used. Just curious as to why would it work with API secret and API key swapped for some requests?

Those endpoints most likely don’t require authentication and therefore ignored the erroneous signature. I explained earlier how I had the same issue a while back, after fixing those endpoints still work fine with the updated signature

Ahhhhhh, That makes so much sense. I was pretty much showing up to a door with the wrong key, but the door didn’t need a key and I was lucky in getting through.
Thanks again!

image

1 Like

Swapping the Secret and Public Key didn’t work and looking at the python documentation " hmac. new ( key , msg=None , digestmod=’’ )

Return a new hmac object. key is a bytes or bytearray object giving the secret key."

I really think there is something with the formatting for the payload for the Market Order since according the Coinbase documentation:
“The CB-ACCESS-SIGN header is generated by creating a sha256 HMAC using the base64-decoded secret key on the prehash string timestamp + method + requestPath + body (where + represents string concatenation) and base64-encode the output. The timestamp value is the same as the CB-ACCESS-TIMESTAMP header”

Therefore if the body is in correct, the signature would be incorrect as well and i would receive the error I am getting.

After making a market order on the Sandboxed Account manually, I then used the API to get my orders which returned something different than the API documentation example for market orders which is re-enforcing my idea that I’m using the wrong payload. I’m wondering if the payload for Market Orders should look similar to:

{‘product_id’: ‘BTC-USD’,
‘profile_id’: ‘765c9f36-2739-4c47-a3e8-86f42a68db52’,
‘side’: ‘buy’,
‘funds’: ‘49.7512437800000000’,
‘type’: ‘market’,
‘post_only’: False,
},

P.S-The profile ID above has been altered

Your payload should look similar to this for a straight market buy

CoinbaseAPIResponse marketBuyResponse = cbAccount.postRequest("/orders",
String.join("", “{“type”:“market”,“side”:“buy”,“post_only”:“false”,“product_id”:”",
this.PRODUCT_ID , “”,“funds”:"", spendableBalance ,""}"));

I hate that it doesn’t show the backslash, those aren’t really doubled quotes…

1 Like

@cleggink @SubStandard - I pulled together some example code here:

import json, hmac, hashlib, time, requests, base64
from requests.auth import AuthBase

Create custom authentication for Exchange

class CoinbaseExchangeAuth(AuthBase):
def init(self, api_key, secret_key, passphrase):
self.api_key = api_key
self.secret_key = secret_key
self.passphrase = passphrase

def __call__(self, request):
    timestamp = str(time.time())
    message = timestamp + request.method + request.path_url + str(request.body or '')

    hmac_key = base64.b64decode(self.secret_key)
    signature = hmac.new(hmac_key, message.encode(), hashlib.sha256)
    signature_b64 = base64.b64encode(signature.digest()).decode()

    request.headers.update({
        'CB-ACCESS-SIGN': signature_b64,
        'CB-ACCESS-TIMESTAMP': timestamp,
        'CB-ACCESS-KEY': self.api_key,
        'CB-ACCESS-PASSPHRASE': self.passphrase,
        'Content-Type': 'application/json'
    })
    return request

EXCHANGE_API_URL = “https://api.exchange.coinbase.com/
auth = CoinbaseExchangeAuth(EXCHANGE_API_KEY, EXCHANGE_API_SECRET, EXCHANGE_API_PASS)

Place an order

new_order = {
‘type’: ‘market’,
‘side’: ‘buy’,
‘post_only’ : ‘false’,
‘product_id’: ‘BTC-USD’,
‘size’ : ‘.000016’
}
r = requests.post(EXCHANGE_API_URL + ‘orders’, data=json.dumps(new_order), auth=auth)
print (r.json())

See your request is missing the accept header I mentioned a while back

2 Likes