This section describes the steps required of the client to place an order, and the interactions between the client and server to execute an order once a match has been found. See the atomic settlement section for a high-level overview of the settlement process.
There are three available types of order.
- Limit orders are used to buy or sell a specified amount of an asset at a rate no higher (buy) or lower (sell) than a specified price.
- Market orders are used to buy or sell a specified amount of an asset at the best price available.
- Cancel orders are used to remove standing limit orders from the order book.
Regardless of connection status, if a client does not respond to their
preimage
request, they are in violation of
rule 1 and subject to
penalty.
If a client's connection is lost during match negotiation, the client is expected to reconnect and complete settlement. Once a match is made, a client is always subject to violation of rule 2 via the broadcast timeout.
An order book can be viewed and tracked by subscribing to a market.
Request route: orderbook
, originator: client
payload
field | type | description |
---|---|---|
base | int | the base asset ID |
quote | int | the quote asset ID |
The response will contain the complete market order book. The order book and all updates include a sequence ID, which increments by +1 whenever the DEX accepts, removes, or modifies an order. The client is responsible for tracking the sequence ID to ensure all order updates are received. If an update appears to be missing, the client should re-subscribe to the market to synchronize the order book from scratch.
Response
payload
field | type | description |
---|---|---|
marketid | string | A unique market identifier, the market ID. |
seq | int | A sequence ID |
epoch | int | the current epoch |
orders | [object] | A list of Order objects (described below) |
JSON Order object
field | type | description | notes |
---|---|---|---|
seq | int | A sequence ID |
superceded in orderbook response
|
marketid | string | the market ID | |
oid | string | the order ID | |
side | string | "b" for buy, "s" for sell | epoch_order, book_order |
qty | int | order size (atoms) | epoch_order, book_order |
rate | int | price rate. message-rate encoding | only set on limit orders |
tif | string | time in force. one of "i" for immediate or "s" for standing | only set on limit orders |
time | int | the order's UNIX timestamp | epoch_order, book_order |
com | string | the order commitment | epoch_order only |
otype | string | "l" for limit, "m" for market, "c" for cancel | epoch_order only |
epoch | int | the order's epoch index | epoch_order only |
target | string | target order ID | only set on cancel orders (epoch_order only) |
Changes to the order book will be received from the DEX as a stream of notifications. The action to be taken depends on the route according to the following table. The payload for all three routes is an Order object.
route | action |
---|---|
book_order | add (or update) the order to the order book |
epoch_order | add the order to the epoch queue |
unbook_order | remove the order from the order book |
Additionally, there is a notification for updating the remaining quantity of an order. This notification is sent when a booked order partially fills and remains on the book with a reduced matchable quantity.
Notification route: update_remaining
, originator: DEX
payload
field | type | description |
---|---|---|
seq | int | A sequence ID |
marketid | string | The market identifier |
oid | string | The order ID |
remaining | int | remaining quantity (atoms) |
At the beginning of the matching cycle, the DEX will publish a list of order preimages, the seed hash used for order sequencing, and the commitment checksum, which together can be used to independently verify matching.
Notification route: match_proof
, originator: DEX
payload
field | type | description |
---|---|---|
marketid | string | the market ID |
epoch | int | the epoch index for which the cycle occurs |
preimages | [string] | list of order preimages for the epoch |
misses | [string] | list of order IDs for which preimages were not received, so were not included in sorting or matching |
csum | string | the commitment checksum |
seed | string | epoch queue shuffling seed |
A client can unsubscribe from order book updates without closing the WebSocket connection.
Request route: unsub_orderbook
, originator: client
payload
field | type | description |
---|---|---|
marketid | string | the market ID |
result
: boolean true
on success.
As part of the order, the client must demonstrate control of funds. This is accomplished by supplying information and a signature for each coin that will be spent. The client covers the backing fees associated with the inputs spending their own coins.
In addition, the client must show the ability to cover base fees for any initialization transactions that will be generated. The client must show that they have the funds to cover all fees for the worst-case scenario, which is single-lot matches for the entire order. In practice, large orders will rarely pay the total of the base fees because many of the matches will be more than a single-lot.
The base fees cover transaction fees associated with making initialization transactions for every match in the order.
For asset Z, a base fee ratio, Rz is calculated based on the lot size, l (units Z), a fee rate, r (Z/byte), and a transaction size, s (bytes). s is pre-calculated based on a standard initialization transaction.
The base fee ratio is a constant until the DEX operator changes one of its factors.
The base fees, fbase (units Z) can be calculated from Rz and the order quantity, Q.
The base fees scale linearly with order size, but the actual realized portion of the base fees, ffin, can only be known to fall within a range r s ≤ ffin ≤ fbase .
The client also covers the backing fees associated with spending their backing coins, fcoin. The client must know how to calculate the script sizes to assess fees. The DEX will verify the coin sum before accepting the order.
With the exception of market buy orders, which are detailed below, for an order of quantity Q, the sum value of the selected coins, V, must satisfy the relation (with fees)
There may be types of coins which are not supported by the asset's DEX implementation. Asset developers should make coin-spending limitations clear to wallet users.
As part of the order, the client will submit a list of Coin objects.
JSON Coin object
field | type | description |
---|---|---|
coinid | string | hex-encoded coin ID |
pubkeys | [string] | array of hex-encoded pubkeys which spend the coin |
sigs | [string] | array of signatures of Blake-256 hashes of the serialized coin IDs |
redeem | string | hex-encoded redeem script for P2SH. empty for P2PKH |
In order to enable multi-signature support, more than one pubkey can be
submitted. If more than one pubkey is submitted, there should be a signature
for each one.
The data is signed with the private key(s) corresponding to the
pubkeys
.
The pubkeys
themselves must correspond with addresses payable by
the coin's pubkey script (or corresponding redeem script).
As part of every submitted order, the client should submit a cryptographic commitment. To generate a commitment, the client creates a random 32-byte sequence, the preimage. The commitment is the Blake-256 hash of the preimage. Every order must be assigned a unique commitment, therefore preimages cannot be reused. They should be generated with a cryptographically-secure pseudo-random number generator.
The server will reject any zero-valued commitment as well as the specific commitment generated from hashing a zero-valued preimage. The server can also reject a commitment that has already been used for any previous order. Depending on server policy, the historical commitment duplicate search may be limited to only recent commitments and/or commitments received for orders on a specific market. Assuming that a cryptographically-secure PRNG is used to generate preimages, rejection should realistically be impossible.
At the expiration of the epoch, the server sends a preimage
request
for each order that is eligible for matching.
The client responds with their preimage(s). If the client fails to respond to
their preimage
requests, or if their preimage
response
does not hash to their order commitment, the order is not matched and the client
is considered in violation of
rule 1.
The preimages are used as the inputs to
the shuffling algorithm to determine
matching order. Before matching commences, the preimages are broadcast
in the match_proof
message.
All orders must be signed by the client and the server. The basic signing procedure will involve serializing order data into a byte array following a specific procedure that can be replicated on the server. The serialized data is then signed using the client's private account key.
All integer encoding for all serialized structures is big endian.
All order serializations have common prefix fields.
Prefix fields and serialization
field | size (bytes) | JSON type | description |
---|---|---|---|
accountid | 32 | string | hex-encoded client account ID |
base | 4 | int | the base asset ID |
quote | 4 | int | the quote asset ID |
ordertype | 1 | int | the type of order. limit = 1, market=2, cancel=3 |
tclient | 8 | int | the client's UNIX timestamp (milliseconds) |
tserver | 8 | int | the server's UNIX timestamp (milliseconds). zero for client signature |
com | 32 | string | hex-encoded cryptographic commitment |
The order serialization is used to create a unique order ID. The ID is defined as the Blake-256 hash of the serialized order, including the non-zero server's timestamp. The client does not know the order ID when submitting, but should independently verify the ID after parsing the server's response.
Because the order ID includes the server's timestamp, the order ID itself provides a checksum to ensure that order information is properly transmitted. The response to all order submissions is an order receipt, which includes the timestamp.
Order receipt
result
field | type | description |
---|---|---|
sig | string | server hex-encoded signature of the serialized order, after adding the DEX timestamp |
orderid | string | the order ID |
tserver | int | the server's UNIX timestamp (milliseconds) |
The client should use the server's timestamp to create a serialized order and independently verify the order ID. The serialized order is also the message for the server's signature.
Limit orders are for the trade of assets at a rate no higher (buy) or lower
(sell) than a specified price.
The client may specify the time in force of a limit order as one of: (a)
standing, which remains on the books until filled or canceled, or (b)
immediate, which can complete execution wholly or partially unfilled. As
such, the immediate option is intended for limit orders with a price that
crosses the spread (i.e. a taker rather than a maker). The
ordersize
must be an integer multiple of the asset's
lot size.
Request route: limit
, originator: client
payload
field | type | description |
---|---|---|
9 prefix fields | ||
side | int | buy = 1, sell = 2 |
ordersize | int | order size (atoms) |
rate | int | price rate. message-rate encoding |
timeinforce | int | standing = 1, immediate = 2 |
coins | [Coin] | array of funding coins |
address | string | address where the matched client will send funds |
sig | string | client hex-encoded signature of the serialized order, with tserver = 0 |
Limit order serialization
field | size (bytes) | description |
---|---|---|
prefix | 99 | the order prefix |
coin count | 1 | The number of funding coins |
coin data | coin length x count | sequence of coin IDs |
side | 1 | 1 for buy, 2 for sell |
quantity | 8 | quantity to buy or sell (atoms) |
rate | 8 | price rate. message-rate encoding |
time in force | 1 | 1 for standing, 2 for immediate |
address | varies | client's receiving address |
result
field | type | description |
---|---|---|
sig | string | server hex-encoded signature of the serialized order, after adding the DEX timestamp |
server time | int | the server's UNIX timestamp (milliseconds) |
A market order is an order to buy or sell an asset at the best available
market price. The request payload fields are similar to a limit order, but
without the rate
field or timeinforce
fields.
Market orders cannot be canceled. Any portion of the requested quantity that does not match immediately (during the epoch match cycle) is left unfilled.
Request route: market
, originator: client
payload
field | type | description |
---|---|---|
9 prefix fields | ||
side | int | buy = 1, sell = 2 |
ordersize | int | order size (atoms) |
coins | [Coin] | array of funding coins |
address | string | address where the matched client will send funds |
sig | string | client hex-encoded signature of the serialized order, with tserver = 0 |
Market order serialization
field | size (bytes) | description |
---|---|---|
prefix | 99 | the order prefix |
coin count | 1 | The number of funding coins |
coin data | coin length x count | sequence of coin IDs |
side | 1 | 1 for buy, 2 for sell |
quantity | 8 | quantity to buy or sell (atoms) |
address | varies | client's receiving address |
result
field | type | description |
---|---|---|
sig | string | server hex-encoded signature of the order by server, after adding the DEX timestamp |
server time | int | the server's UNIX timestamp (milliseconds) |
Market buy orders have a slightly different ruleset than market sell orders or
limit orders.
First, the ordersize
is not denominated in the base asset, but in
the quote asset.
As an example, on the DCR/BTC market, where DCR is the base asset, market sell
orders and both types of limit orders' ordersize
are quantified in
the base asset, DCR, but the market buy order's ordersize
is in BTC.
The order is essentially a statement of "buy as much DCR as you can with this
much BTC".
The ordersize
is also not bound to the integral lot size
constraints of other types of orders.
Since the market may move before the order is matched, at the time of submission it is not known with certainty how many lots will match. For orders that are nearly 1 lot, it is possible for no matching to occur because by the time the order is matched it cannot afford a single lot. The DEX server maintains an interest in ensuring that only valid orders that can match are accepted, so market buy orders must be handled carefully to make sure they remain valid.
To reduce the possibility of market buy orders becoming invalid (too small to
match) due to a price increase, the DEX operator sets a market buy buffer,
bm > 1 for each market.
For a market where the base asset has lot size l, and for which there
is a best known standing sell order price rate, r, the
ordersize
, Q, must satisfy the relation
Q > bm l r.
If the best rate increases before the order is matched, the order will still
result in a fill as long as the price does not surpass
~bm r.
If the market buy buffer is set too low or the market is particularly
volatile and the price exceeds bm r, an order that was
accepted but is now too small to match is considered executed but unfilled and
there is no change to the account's
cancellation statistics.
Cancel orders remove standing limit orders from the order book. A client cannot cancel a market order or a limit order with time in force immediate. Further, due to the epoch-based pseudorandom matching process, a cancel order submitted in the same epoch as it's corresponding limit order has a 50% chance of being processed before the order it cancels, resulting in an error. This is by design and discourages certain types of spoofing.
Request route: cancel
, originator: client
payload
field | type | description |
---|---|---|
9 prefix fields | ||
targetid | string | hex-encoded order ID |
sig | string | client hex-encoded signature of the serialized order data. serialization described below |
Cancel order serialization
field | size (bytes) | description |
---|---|---|
prefix | 99 | the order prefix |
orderid | 32 | the order ID |
result
field | type | description |
---|---|---|
sig | string | server hex-encoded signature of the serialize order data, after adding the DEX timestamp |
tserver | int | the server's UNIX timestamp (milliseconds) |
At the expiration of the epoch, the DEX sends out a preimage
request for each order in the epoch queue. The match cycle begins 5 seconds
after the last preimage
request is sent by the server, so clients
must respond before then.
A commitment checksum is included as part of the
preimage
request.
The checksum is the Blake-256 hash of the concatenated, lexicographically-sorted
commitments for every order in the epoch. For clients subscribed to the order
book for the entire duration of the epoch, the checksum can be validated against
the checksum generated from their local copy of the epoch queue.
Request route: preimage
, originator: DEX
payload
field | type | description |
---|---|---|
orderid | string | order ID |
csum | string | the commitment checksum |
Preimage response
result
field | size (bytes) | description |
---|---|---|
pimg | string | hex-encoded preimage for the order's commitment |
Swap negotiation details will be relayed through the DEX with a series of
notifications.
Both the DEX and the clients will need to serialize and sign the notification
data. The originator includes their signature with the request, while the
recipient will return an acknowledgement, or a list of
acknowledgements, as the result
of their response payload.
Acknowledgement
field | type | description |
---|---|---|
matchid | string | the match ID |
sig | string | hex-encoded signature of the notification data |
If the client's order has one or more matches at the end of a match cycle, the
DEX will send a list of match objects. The maker is the first to act, so
after sending their acknowledgement, they should broadcast their initialization
transaction and inform the server with an init
notification
(described after).
Request route: match
, originator: DEX
payload
(array)
field | type | description |
---|---|---|
orderid | string | order ID |
matchid | string | the match ID to use for progress notifications |
qty | int | the matched amount, in atoms of the base asset |
rate | int | the matched price rate. message-rate encoding |
tserver | int | server's UNIX timestamp (milliseconds) |
address | string | the counterparty's receiving address |
side | int | the client's side in the match. 0 = maker, 1 = taker |
status | int | only provided in 'connect' response. For 'match' requests, status is 0 = 'MakerSwapCast'. See match.go for codes. |
sig | string | DEX's hex-encoded signature of the serialized notification data. serialization described below |
Match serialization
field | size (bytes) | description |
---|---|---|
orderid | 32 | the order ID |
matchid | 32 | the ID assigned to this match |
quantity | 8 | the matched amount, in atoms of the base asset |
rate | 8 | the matched price rate. message-rate encoding |
tserver | 8 | server's UNIX timestamp (milliseconds) |
address | varies | UTF-8 encoded receiving address for the match |
The tserver
value is used as the basis for the the locktimes.
If it is necessary to convert the time to seconds, the value should be rounded
down.
The client will respond with a list of signed match acknowledgements.
After a client broadcasts their initialization transaction, they are expected to report the transaction details to the server for verification and relay to the matching party.
Request route: init
, originator: client
payload
field | type | description |
---|---|---|
orderid | string | the order ID |
matchid | string | the matchid, retrieved from the match notification |
coinid | string | hex-encoded coin ID |
contract | string | hex-encoded swap redeem script |
sig | string | client signature of the serialized notification. serialization described below |
Init serialization
field | size (bytes) | description |
---|---|---|
orderid | 32 | the order ID |
matchid | 32 | the ID assigned to this match |
coinid | asset-dependent | the coin ID |
contract | asset-dependent | swap redeem script |
The DEX will respond with an acknowledgement.
The DEX will send each client a notification when the counterparty has broadcast
their initialization transaction.
When the taker receives the audit
notification, they will audit the
maker's contract and after that contract asset's configured swap confirmation
requirement is reached, broadcast their own initialization.
When the maker receives the audit
notification, they will audit the
taker's contract and after the required confirmations, issue their redemption.
Request route: audit
, originator: DEX
payload
field | type | description |
---|---|---|
orderid | string | the order ID |
matchid | string | the match ID |
timestamp | int | server's UNIX timestamp (milliseconds) |
coinid | string | hex-encoded coin ID |
contract | string | hex-encoded swap redeem script |
sig | string | DEX's signature of the serialized notification. serialization described below |
Audit serialization
field | size (bytes) | description |
---|---|---|
orderid | 32 | the order ID |
matchid | 32 | the match ID |
timestamp | 8 | server's UNIX timestamp (milliseconds) |
coin ID | asset-dependent | the coin ID |
contract | asset-dependent | swap redeem script |
The client responds with an acknowledgement.
When a client has redeemed their contract, they will notify the server.
Request route: redeem
, originator: client
payload
field | type | description |
---|---|---|
orderid | string | the order ID |
matchid | string | the match ID |
coinid | string | hex-encoded coin ID |
secret | string | the hex-encoded swap contract secret |
sig | string | client signature of the serialized notification. serialization described below |
Redeem serialization
field | size (bytes) | description |
---|---|---|
orderid | 32 | the order ID |
matchid | 32 | the match ID |
coin ID | asset-dependent | the coin ID |
secret | 32 | the swap contract secret |
The DEX responds with an acknowledgement.
The DEX informs the taker when the maker has redeemed.
Request route: redemption
, originator: DEX
payload
field | size (bytes) | description |
---|---|---|
orderid | 32 | the order ID |
matchid | 32 | the match ID |
coinid | string | hex-encoded coin ID |
secret | string | the hex-encoded swap contract secret |
timestamp | int | server's UNIX timestamp (milliseconds) |
sig | string | DEX's signature of the serialized notification. serialization described below |
Redemption serialization
field | size (bytes) | description |
---|---|---|
orderid | 32 | the order ID |
matchid | 32 | the match ID |
coin ID | asset-dependent | the coin ID |
secret | 32 | the swap contract secret |
timestamp | 8 | server's UNIX timestamp (milliseconds) |
The client will respond with an acknowledgement.
The taker will get the key from the maker's redemption and broadcast their own redemption transaction.
It is also possible for an epoch order to go through the matching cycle without
generating a match. This will be common for limit orders, but can also occur for
market orders if there are no booked orders to match with. When the server fails
to find any matches, a nomatch
notification will be sent to the
client.
Notification route: nomatch
, originator: DEX
payload
field | type | description |
---|---|---|
orderid | string | order ID |
A client can request the current match status using the
match_status
request. Note that the route is provided as a
convenience, and persistence of match data may be subject to the operator's
archiving policy. match_status
can be used to recover after a
temporary disconnection, but should not be relied on as a source of historical
match data.
Notification route: match_status
, originator: client
payload
: an array of MatchRequest
objects
MatchRequest
object
field | type | description |
---|---|---|
base | int | the base asset ID |
quote | int | the quote asset ID |
matchid | string | hex-encoded Match ID |
result
: an array of MatchStatus
objects
MatchStatus
object
field | type | description |
---|---|---|
matchid | string | the match ID |
status | int | See match.go for codes. |
makercontract | string | the swap contract broadcast by the maker |
takercontract | string | the swap contract broadcast by the taker |
makerswap | string | the coin ID for the swap broadcast by the maker |
takerswap | string | the coin ID for the swap broadcast by the taker |
makerredeem | string | the coin ID for the maker's redemption |
takerredeem | string | the coin ID for the taker's redemption |
secret | string | the swap contract secret |
active | bool | whether the match is still active |
Empty fields may be omitted from the encoded MatchStatus
object.
Results are only returned for matches that could be found in the server
records. No error is set in the case of unfound matches.
A match can be revoked by the server if a client fails to act within the broadcast timeout. A match revocation will result in penalties for the violating party only. The revoked match quantity is not added back to the order book in any form.
Request route: revoke_match
, originator: DEX
payload
field | type | description |
---|---|---|
orderid | string | the order ID |
matchid | string | the match ID |
sig | string | DEX's hex-encoded signature of serialized revocation. serialization described below |
Revocation serialization
field | size (bytes) | description |
---|---|---|
orderid | 32 | the order ID |
matchid | 32 | the match ID |
The client will respond with an acknowledgement.
There are a number of scenarios where the server may suspend operations, intentionally or not. During trade suspension, standing limit orders are not necessarily revoked.
If the server intentionally suspends operations, they should provide a notification to connected clients as early as possible, ideally with several epochs for the client to get their orders situated before matching ceases.
Clients should be prepared to lose connection during suspension. Clients will need to reconnect and complete settlement when the server comes back online.
If the server disconnects without notice, it is expected that orders placed during the current epoch are revoked at no penalty to the client and that standing limit orders are persisted.
The suspension notification may indicate that standing limit orders will not be persisted. This would be the case if the DEX needs to change an asset variable such as the lot size or minimum transaction fee rate.
If standing limit orders are persisted, they will be auto-revoked if the client does not reconnect before the next start epoch.
Request route: suspension
, originator: DEX
payload
field | type | description |
---|---|---|
marketid | string | the market ID |
finalepoch | uint64 | the last epoch during which orders will be collected and matched |
suspendtime | uint64 | the UNIX timestamp corresponding to the end of the final epoch (milliseconds) |
persistbook | bool | whether standing limit orders will persist through the suspension |
Clients will also be informed when trading is resumed.
Request route: resumption
, originator: DEX
payload
field | type | description |
---|---|---|
marketid | string | the market ID |
startepoch | uint64 | the epoch number at which trading did or will commence. May be in the future |
epochlen | uint64 | the epoch duration (milliseconds) |