Order Signature

The libraries exposes methods to allows user to generate order signatures. These signatures are used to verify authenticity of the order at the time of trade being settled on-chain.

When an order submitted by a user is routed through the order matching engine, the matching engine, not the user, will submit the order on-chain as part of a trade and therefore the signature is critical to verify the authenticity of the user's order request.

OrderbookOnly We do not expose methods in the public libraries to submit a trade call directly onchain. Just in case if you decide to place a trade directly onchain then the individual orders which make up that trade must have orderbookOnly flag set to False in the signature request.

Sub Accounts: If trading as a sub-account, set the maker field to the address of the parent account. Read more about Sub Accounts here.

.The method takes the following input parameters:

  • enum TIME_IN_FORCE {
        IMMEDIATE_OR_CANCEL = "IOC",
        GOOD_TILL_TIME = "GTT"
    }
    
    interface RequiredOrderFields {
      symbol: MarketSymbol; // market for which to create order
      price: number; // price at which to place order. Will be zero for a market order
      quantity: number; // quantity/size of order
      side: ORDER_SIDE; // BUY/SELL
      orderType: ORDER_TYPE; // MARKET/LIMIT
      triggerPrice?: number; // optional, send triggerPrice for stop orders
      postOnly?: boolean; // true/false, default is false
      orderbookOnly: bool # must be set to true to allow for an order to be posted to bluefin orderbook. 
      timeInForce?: TIME_IN_FORCE; // IOC/GTT by default all orders are GTT
    }
    
    interface OrderSignatureRequest extends RequiredOrderFields {
      leverage?: number; // leverage to take, default is 1
      reduceOnly?: boolean; // is order to be reduce only true/false, default its false
      salt?: number; // random number for uniqueness of order. Generated randomly if not provided
      expiration?: number; // time at which order will expire. Will be set to 1 month if not provided
      maker?: address; // address of the parent account on behalf user wants to place the order
      isBuy?: boolean;
    }
    
    
    class RequiredOrderFields(TypedDict): 
      symbol: MARKET_SYMBOLS # market for which to create order
      price: int # price at which to place order. Will be zero for a market order
      quantity: int # quantity/size of order
      side: ORDER_SIDE # BUY/SELL
      orderType: ORDER_TYPE # MARKET/LIMIT
    
    class OrderSignatureRequest(RequiredOrderFields): 
      leverage: int # (optional) leverage to take, default is 1
      reduceOnly: bool # (optional)  is order to be reduce only true/false, default its false
      postOnly: bool # (optional)  is order to be post only true/false, default its false
      orderBookOnly: bool # must be set to true to allow for an order to be posted to bluefin orderbook. 
      salt: int # (optional)  random number for uniqueness of order. Generated randomly if not provided
      expiration: int # (optional) unix timestamp in ms at which order will expire. Will be set to 1 month if not provided
      maker: str # (optional) maker of the order, if not provided the account used to initialize the client will be default maker
    

All number inputs are expected in base format and will be converted to big number strings internally by the library.

The method returns an Order Signature Response with the following fields:

  • interface OrderSignatureRequest extends RequiredOrderFields {
      leverage?: number;
      reduceOnly?: boolean;
      salt?: number;
      expiration?: number;
      maker?: address;
      isBuy?: boolean;
    }
    
    interface OrderSignatureResponse extends RequiredOrderFields {
      leverage: number;
      reduceOnly: boolean;
      salt: number;
      expiration: number;
      orderSignature: string;
      maker: address;
    }
    
    
    class OrderSignatureResponse(OrderSignatureRequest): 
      	maker: str
    	  orderSignature: str # generated order signature
    

Examples

/**
 * Create an order signature on chain and returns it. The signature is used to verify
 * during on-chain trade settlement whether the orders being settled against each other
 * were actually signed on by the maker/taker of the order or not.
 */

/* eslint-disable no-console */
import {
  ORDER_STATUS,
  ORDER_SIDE,
  ORDER_TYPE,
  toBaseNumber,
  MinifiedCandleStick,
  Faucet,
  OrderSigner,
  parseSigPK,
  ADJUST_MARGIN,
  Networks, 
  BluefinClient
} from "@bluefin-exchange/bluefin-v2-client";

async function main() {
  // no gas fee is required to create order signature.
  const dummyAccountKey =
    "trigger swim reunion gate hen black real deer light nature trial dust";

  const client = new BluefinClient(
    true,
    Networks.TESTNET_SUI,
    dummyAccountKey,
    "ED25519" //valid values are ED25519 or Secp256k1
  ); // passing isTermAccepted = true for compliance and authorization
  await client.init();
  let symbol = "ETH-PERP";

  try {
    client.createSignedOrder({
      symbol: symbol,
      price: 0,
      quantity: 0.1,
      side: ORDER_SIDE.SELL,
      orderType: ORDER_TYPE.MARKET,
    });
  } catch (e) {
    console.log("Error:", e);
  }

  // will create a signed order to sell 0.1 DOT at MARKET price
  const signedOrder = await client.createSignedOrder({
    symbol: symbol, // asset to be traded
    price: 0, // 0 implies market order
    quantity: 0.1, // the amount of asset to trade
    side: ORDER_SIDE.SELL, // buy or sell
    orderType: ORDER_TYPE.MARKET,
  });

  console.log("Signed Order Created:", signedOrder);
}

main().then().catch(console.warn);

from pprint import pprint
import asyncio
import time
from config import TEST_ACCT_KEY, SUI_STAGING
from bluefin_v2_client import (
    BluefinClient,
    Networks,
    MARKET_SYMBOLS,
    ORDER_SIDE,
    ORDER_TYPE,
    OrderSignatureRequest,
)


async def place_orders(client: BluefinClient):
    # default leverage of account is set to 3 on Bluefin
    user_leverage = await client.get_user_leverage(MARKET_SYMBOLS.ETH)
    print("User Default Leverage", user_leverage)

    # Sign and place a limit order at 4x leverage. Order is signed using the account seed phrase set on the client
    adjusted_leverage = 4
    await client.adjust_leverage(MARKET_SYMBOLS.ETH, adjusted_leverage)
    signature_request = OrderSignatureRequest(
        symbol=MARKET_SYMBOLS.ETH,  # market symbol
        price=1636.8,  # price at which you want to place order
        quantity=0.01,  # quantity
        side=ORDER_SIDE.BUY,
        orderType=ORDER_TYPE.LIMIT,
        leverage=adjusted_leverage,
        expiration=int(
            (time.time() + 864000) * 1000
        ),  # expiry after 10 days, default expiry is a month
    )
    signed_order = client.create_signed_order(signature_request)
    resp = await client.post_signed_order(signed_order)
    pprint({"msg": "placing limit order", "resp": resp})

    # sign and place a market order at 2x leverage
    adjusted_leverage = 2
    await client.adjust_leverage(MARKET_SYMBOLS.BTC, adjusted_leverage)
    signature_request = OrderSignatureRequest(
        symbol=MARKET_SYMBOLS.BTC,
        price=0,
        quantity=1,
        leverage=adjusted_leverage,
        side=ORDER_SIDE.BUY,
        reduceOnly=False,
        postOnly=False,
        orderbookOnly=True,
        orderType=ORDER_TYPE.MARKET,
    )
    signed_order = client.create_signed_order(signature_request)
    resp = await client.post_signed_order(signed_order)
    pprint({"msg": "placing market order", "resp": resp})
    return


async def main():
    # initialize client
    client = BluefinClient(
        True,  # agree to terms and conditions
        Networks[SUI_STAGING],  # network to connect with
        TEST_ACCT_KEY,  # seed phrase of the wallet
    )

    await client.init(True)
    await place_orders(client)

    await client.close_connections()


if __name__ == "__main__":
    loop = asyncio.new_event_loop()
    loop.run_until_complete(main())
    loop.close()

Order Signatures Overview

To post an order to the API, you may choose any other language or library that allows you to request HTTP. While we don't support official libraries for other languages, you can find a list of order signature creation examples in various languages here.

Below is the pseudo code to translate the order signature examples to different languages:

Step 1: Encoding Flags
Encode all the boolean flags into a number where each bit of the number represents if a particular flag is turned on or off.

0th bit = ioc  
1st bit = postOnly  
2nd bit = reduceOnly  
3rd bit  = isBuy  
4th bit = orderbookOnly  || e.g. 00000000 all flags false | e.g. 00000001 ioc order, sell side, can be executed by taker ||  e.g. 00010001 // same as above but can only be executed by settlement operator

If an order is of type buy and is being signed for the orderbook, the encoded order flag value will be 24 which is represented as 0001_1000 in binary.

Step 2: Order serializaiton
Serialize the order payload to be signed. The structure of the serialized order is as follows:

serializedOrder  
[0,15]     => price            (128 bits = 16 bytes)  
[16,31]    => quantity         (128 bits = 16 bytes)  
[32,47]    => leverage         (128 bits = 16 bytes)  
[48,63]    => salt             (128 bits = 16 bytes)  
[64,71]    => expiration       (64 bits = 8 bytes)  
[72,103]   => maker            (256 bits = 32 bytes)  
[104,135]   => market          (256 bits = 32 bytes)  
[136,136]  => flags       (1 byte)  
[137,143]  => domain (Bluefin) (7 bytes)  

The first 16 bytes will contain the price, the next 16 quantity and so on. All numerical values (except encoded order flags created in step 1) are to be converted into their hex string representation. All hex values should be of 32 length as on-chain each byte is used to represent 2 characters so 16 bytes == 32 characters. The encoded order flags are to be converted into its hex and padded with a single 0 on the left if the resultant hex is of 1 digit to make the length of the flags to be two as the flag is of 1 byte on-chain and 1 byte = 2 characters.

Step 3: Encoding, Hashing and Signing

  1. Convert the serialized order created in step 2 into its Uint8 representation using a UTF encoder.
  2. Take sha256 hash of the uint8 array representation of serialized order.
  3. Sign the sha256 hash.
  4. Convert the resultant uin8 signature into its hex representation to create a string
  5. If the wallet being used to sign the signature is of
    • secp256K1 scheme, append 0 at the end of signature hex string.
    • ed25519 scheme, append 1 at the end of the signature hex string