Order Signature Example

Orderly Network uses the Secp256k1 with the ECDSA algorithm to encrypt orders. Here we provide a simple example that shows you how to send a valid order to Orderly Network.

Requests sent to the following endpoints will require an order signature:

POST /v1/order

POST /v1/batch-order

PUT /v1/order

DELETE /v1/order

DELETE /v1/client/order

DELETE /v1/orders

Assume the following information:

KeyValue
orderly-trading-key90b8d328cde365b3dd10b194048b677d575c2faf51790ecfa6c2fe8b0403324984b275e7bf4c486b4d713576cf20335e1230537c47aafdde0bd646af9b83a8d6
orderly-trading-secretae88e5d3b3b37d2bdb7254e798fc3756a3e5a726df086089ef6e7835f08be794

Hash your request parameters with orderly-trading-secret. The hashing logic is as below:

First, normalize the order content:

  1. Remove any null fields from the order parameters (or cancel parameters)
  2. Remove any trailing zeros for any numeric order parameter (eg. 150.00 > 150)
  3. Sort order parameters in alphabetical order
  4. Concatenate order parameters in query string format.

Then use orerly-trading-secret to generate a signature using the Secp256k1 algorithm from the bytes of the normalized content. Note we are using the format as below:

  1. Find out the (R, S, V) values of the signature
  2. Concatenate R with S
  3. Concatenate V with the above string, with V being in (00, 01, 02, 03). If your V value is in (27, 28, 29, 30), subtract 27 first.

The total length of the signature (in hex) should be 64 + 64 + 2 = 130 characters. Please pad leading zeros to each of R and S if their length is less than 64 characters.

Put the signature as a parameter signature, and provide the orderly trading key in the request header as orderly-trading-key.

So the final order parameters would look like:

{
  "symbol": "SPOT_NEAR_USDC.e",
  "order_type": "LIMIT",
  "order_price": 15.23,
  "order_quantity": 23.11,
  "side": "BUY",
  "signature": "fc3c41d988dd03a65a99354a7b1d311a43de6b7a7867bdbdaf228bb74a121f8e47bb15ff7f69eb19c96da222f651da53b5ab30fb7caf69a76f01ad9af06c154400"
}

Then use orerly-trading-secret to generate a signature using the Secp256k1 algorithm:

import eth_keys
def sign_order(self, params: json, trading_key: str, trading_secret: str):
  query_string = ''    
  if params:
    for k, v in dict(sorted(params.items())).items():
      if type(v) == bool:
        query_string += f'{k}={v}&'
      elif v is not None:
        try:
          query_string += f'{k}={v:.10g}&'
        except ValueError:
          query_string += f'{k}={str(v)}&'

  trading_private_key = eth_keys.keys.PrivateKey(bytes.fromhex(trading_secret))

  signature = trading_private_key.sign_msg(bytes(query_string[:-1], 'utf-8'))
  # a,b,c,d as in description of the algorithm
  a = f'R value:{signature.vrs[1]}'
  b = f'064x:{signature.vrs[1]:064x}'
  c = signature.vrs[1]
  d = type(c)
  signature_str = f'{signature.vrs[1]:064x}{signature.vrs[2]:064x}{signature.vrs[0]:02x}'
  return signature_str

If the request looks like:

POST /v1/order

# Body json:

{
  "symbol": "SPOT_NEAR_USDC.e",
  "order_type": "LIMIT",
  "order_price": 15.23,
  "order_quantity": 23.11,
  "side": "BUY"
}

Normalize request content. The result content would look like following

order_price=15.23&order_quantity=23.11&order_type=LIMIT&side=BUY&symbol=SPOT_NEAR_USDC.e

Request Signature Example

Orderly Network uses the ed25519 algorithm for general system access. Here we provide a simple example that shows you how to send a valid request to Orderly Network.

Assume the following information:

KeyValueDescription
orderly-account-idtestuser.nearNEAR Wallet Account id
orderly-keyed25519:8tm7dnKYkSc3FzgPuJaw1wztr79eeZpN35nHW5pL5XhXretrieve from WOOFi DEX GUI
orderly-secreted25519:VNX6EELQhP4G4Zg8HtTNKjBJoCmMKFQ8es7D33NwauX49eoBiL1GUjBARcMGKPtdjFhWNF36SoCUTzJRWKn789Bretrieve from WOOFi DEX GUI
orderly-trading-key90b8d328cde365b3dd10b194048b677d575c2faf51790ecfa6c2fe8b0403324984b275e7bf4c486b4d713576cf20335e1230537c47aafdde0bd646af9b83a8d6retrieve from WOOFi DEX GUI (required for order requests)
timestamp1649920583000Unix epoch time in milliseconds

Note you must generate the order signature first to complete the full request before you can sign the request itself.

Hash your request parameters with orderly-secret. The hashing logic is as below:

First, normalize the request content:

  1. Take the timestamp of the request
  2. Concatenate it with the request method and request path
  3. If the request body is not empty, concatenate the request body json by stringifying the json body. So the final request would look like:
POST /v1/order HTTP/1.1
Content-Type: application/json
orderly-account-id: testuser.near
orderly-key: ed25519:8tm7dnKYkSc3FzgPuJaw1wztr79eeZpN35nHW5pL5XhX
orderly-trading-key: 90b8d328cde365b3dd10b194048b677d575c2faf51790ecfa6c2fe8b0403324984b275e7bf4c486b4d713576cf20335e1230537c47aafdde0bd646af9b83a8d6
orderly-signature: Z8ZgTnmMcKdfQBW62bLNON2YtMpz9riPvw-V33J3aA7_5VjaKehkQD06iAiagIMxja59bqp9L8zTLGXtBkHCCA==
orderly-timestamp: 1649920583000
{
  "symbol":"SPOT_NEAR_USDC.e",
  "order_type":"LIMIT",
  "order_price":15.23,
  "order_quantity":23.11,
  "side":"BUY",
  "signature":"fc3c41d988dd03a65a99354a7b1d311a43de6b7a7867bdbdaf228bb74a121f8e47bb15ff7f69eb19c96da222f651da53b5ab30fb7caf69a76f01ad9af06c154400"
}

If the request looks like (details on how to generate the order signature can be found above):

POST /v1/order

# Body json:
{
  "symbol":"SPOT_NEAR_USDC.e",
  "order_type":"LIMIT",
  "order_price":15.23,
  "order_quantity":23.11,
  "side":"BUY",
  "signature": "fc3c41d988dd03a65a99354a7b1d311a43de6b7a7867bdbdaf228bb74a121f8e47bb15ff7f69eb19c96da222f651da53b5ab30fb7caf69a76f01ad9af06c154400"
}

Normalize request content, The result content would look like following

1649920583000POST/v1/order{"symbol": "SPOT_NEAR_USDC.e", "order_type": "LIMIT", "order_price": 15.23, "order_quantity": 23.11, "side": "BUY", "signature": "fc3c41d988dd03a65a99354a7b1d311a43de6b7a7867bdbdaf228bb74a121f8e47bb15ff7f69eb19c96da222f651da53b5ab30fb7caf69a76f01ad9af06c154400"}

Then use orderly-secret to generate a signature using the ed25519 algorithm, and encode the signature in base64 url format:

from requests import Request
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
import datetime, json, base64, base58

def sign_request(self, request: Request, account_id: str, orderly_key: str, orderly_secret: str, trading_key: str):
  prepared = request.prepare()
  ts = round(datetime.datetime.now().timestamp() * 1000)

  json_str = ''
  if request.json is not None:
      json_str = json.dumps(request.json)

  orderly_private_key = Ed25519PrivateKey.from_private_bytes(base58.b58decode(orderly_secret)[0:32])

  signature_string = f"{ts}{request.method}{prepared.path_url}{json_str}"
  data_bytes = bytes(signature_string, 'utf-8')

  request_signature = base64.b64encode(orderly_private_key.sign(data_bytes)).decode("utf-8")

  request.headers['orderly-account-id'] = account_id
  request.headers['orderly-key'] = f'ed25519:{orderly_key}'
  request.headers['orderly-signature'] = request_signature
  request.headers['orderly-trading-key'] = trading_key
  request.headers['orderly-timestamp'] = str(ts)
  request.headers['Content-Type'] = 'application/json' if request.json else 'application/x-www-form-urlencoded'
  request.headers['Cache-Control'] = 'no-cache'

  return request
Put the signature in request header as orderly-signature, timestamp as orderly-timestamp, and orderly key as orderly-key. Add the orderly-trading-key in the request header if you are performing a trading action.

Security

There have three layer checker to check if a request is valid. Orderly server only accepts the request that passes all checkers. The checker contains the following:

Request Timestamp checker:

The request would be considered expired and get rejected if the timestamp in orderly-timestamp header have 300+ seconds difference from the API server time.

HMAC Parameter Signature:

The request must have a orderly-signature header that is generate from request parameters and signed with your Orderly secret key.

Order Parameter Signature:

Each order must have a signature parameter that is generated from order parameters and signed with your Orderly trading secret key. This is used to verify each order on chain.