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
- Convert the serialized order created in step 2 into its Uint8 representation using a UTF encoder.
- Take sha256 hash of the uint8 array representation of serialized order.
- Sign the sha256 hash.
- Convert the resultant uin8 signature into its hex representation to create a string
- 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