Signing
Signing is ONLY required for the write-operations listed below. GET requests do not require a signature. Signing needs to be done using a very specific payload structure for each operation. If you do not use the exact order and padding below your requests will fail signature verification. Please refer to this public js repo for examples.
Capital: Withdraw and Transfer
Withdraw
You must sign a payload comprising the following fields, padded to their respective sizes, and in the following order. This signed payload needs to be 32 bytes and be passed that in the signature field:
asset_id: 4 bytesquantity: 8 bytesmax_fees: 8 byteswithdrawal_address: 20 bytes
Transfer
You must sign a payload comprising the following fields and pass that in the signature field:
nonce: 8 bytesasset_id: 4 bytesquantity: 8 bytesdst_account_public_key: 64 bytesmax_fees_percent: 8 bytes
Trade
All write operations (place orders, edit orders, cancel orders requests) require signatures.
Trustless accounts: these signatures use ECDSA with the private key provided per API Key.This is a string of 65 bytes including the recovery ID. Depending on what library you're using, the ECDSA signature should give you a recovery ID byte as well. You then concatenate that bit as the 65th byte.
Exchange-managed accounts: these signatures use HMAC with the private key provided per API Key.
Each operation has a specific method for generating the signature digest that needs to be signed. Each operation is detailed below. All numbers are formatted in big endian.
Signing Place/Edit Order
Structure: The payload for placing or editing orders consists of the following. For editing an order, you can use either the orderId or the nonce field to refer to the order.
nonce: 8 bytes- Current timestamp serving as a unique identifier for your order
- Nonce needs to be a milli-second or micro-second unix timestamp within +- 15 seconds of the current time
- Nonce needs to be unique per account (if you send two orders with the same nonce from the same account, they will be rejected by our API)
Contract ID: 4 bytesQuantity: 8 bytes - See section on asset quantities for how these are computedSide: 4 bytes - Ask: 0 ; Bid: 1Price(Optional): 8 bytes - Only applicable for limit order. See section on asset prices for how these are computedMax Fees Percent: 8 bytes- Maximum fees allowed for order operation
- Denoted in basis points (e.g. 20 => 0.2% fee)
- We can get fee information from /market/exchange-info.
When modifying trigger orders:
- If order has been triggered, you should not set the updatedTriggerPrice field. You can modify the order like a regular limit order
- If the order has not been triggered yet, you should set the updatedTriggerPrice field with the new trigger price
- If the order has not been triggered yet, you can modify market orders to limit orders and vice-versa
- If updatedPrice is present, the order will be modified to a limit order with that price
- If updatedPrice is absent, the order will be modified to a market order
- When triggered, the order ends up at the back of the queue for the price level
Example
Let's assume we want to place the following order and contract information from /exchange-info
//order
{
"symbol": "BTC/USDT-P",
"side": "ASK",
"nonce": 1714701600000000,
"quantity": "1 BTC",
"price": "100,000 USDT",
"max_fees": "0.0005", //5 basis points
}
//contract info from /exchange-info
{
"symbol": "BTC/USDT-P",
"id": 2,
"underlyingDecimals": 10,
"underlyingSymbol": "BTC",
"settlementDecimals": 6,
"settlementSymbol": "USDT",
"status":"LIVE"
}We can now compute quantity and price:
Quantity: 1 BTC x 10^10 = 10,000,000,000 since underlyingDecimals: 10
Price:
100,000 USDT x 2^32 x 10^(10 - 6 = -4) = 42,949,672,960since priceMultiplier:2^32andunderlyingDecimals - settlementDecimals = 10 - 6 = -4Using these numbers, we can compute the elements of the buffer as**:**
Nonce:1714701600000000 => 0x0006178313c38800ContractId:2 => 0x00000002Quantity:10000000000 => 0x00000002540be400Side:ASK => 0x00000000Price:42949672960 => 0x0000000a00000000Fees: For fixed fees like withdraw/deposit, we should usefees * 10^6, one example:1.23=>1230000=>0x00000000012C4B00. For rate fees, we should use ratefees * 10^8, one example:0.0005=>5000=>0x0000000000001388
Concatenating these elements gives us a buffer of:
0x0006178313c388000000000200000002540be400000000000000000a000000000000000000001388
// If exchange managed, we would sign this order using HMAC
const hmacSignature = crypto
.createHmac('sha256', 'YOUR-SECRET-KEY')
.update(buffer)
.digest('hex');
// If trustless account, we would sign using ECDSA
const msgHash = ethers.sha256(buffer).slice(2);
const ecdsaSignature = ec.sign(msgHash, 'hex', { canonical: true });Cancel Order
For order cancellations, you simply need to sign the 64-bit orderId or nonce for the original order. You can use either the order ID or nonce to reference the order.
- For example, the place order operation above could result in a response payload like { "order_id":"579183763093760000"}
- If we wanted to cancel this order, we'd need to sign 579183763093760000 as 8 bytes, which would map to 0x0809ac905ae0a800
- Canceling all orders: You can also cancel all orders by sending an HTTP delete to the /trade/orders endpoint with the account ID, a nonce, and signature signed on the nonce