We propose here a decentralized, non-custodial, trustless, and intermediary-free cryptocurrency exchange platform.
The primary service provided by the decentralized exchange (DEX) is to match clients who would like to trade an asset of one type for an asset of another type. Trades are facilitated by a trustless atomic swap negotiation process carried out with all the cryptographic security guarantees of the respective blockchains.
A custom order matching procedure is offered to mitigate common abusive trading patterns associated with high-frequency trading (HFT) algorithms and other bot-driven trading. Elimination of per-trade fees disincintevizes deceptive bookkeeping practices commonly observed in centralized exchanges.
To encourage open and active development, descriptions of critical protocol components and a full client-server API definition are provided.
Existing cryptocurrency exchange platforms fall into 4 overlapping categories: custodial corporate entities, services, tokens, and blockchains. The vast majority of cryptocurrency trading is currently performed via centralized, custodial exchanges. Since trades at these exchanges take place off-chain, they generally settle instantly to make funds available for further trading. This is a nice feature, but it practically guarantees that the market will be a target of HFT and algorithmic traders, which can be unpredictable and malicious. Additionally, such exchanges earn revenue by collecting trading fees. Such a model creates incentives for an exchange to artificially inflate their volume by wash trading [1]. By one study, it has been estimated that more than 95% of reported exchange volume is faked [2].
Several DEX projects have been created to address some of these issues by replacing the exchange with a blockchain or a token, and they have met with varying degrees of success. While they remove the trusted third party (TTP), they insert their own products as a means to capture the trading fees, which replaces the TTP friction with a new platform friction. The simple act of collecting trading fees serves to act as an incentive to centralize on a given solution, which runs counter to a system of open voluntary exchange. While a chain or token serves to remove the TTP, it also creates challenges with order matching, which typically occurs via the intermediate chain or token.
The DEX described in this document is based on atomic swaps [3], signed and transmitted by the clients. Thus, the exchange of funds is trustless and secure. The purpose of the DEX is to facilitate such peer-to-peer trades, while disincentivizing manipulative or abusive trading patterns, and utilizing an independently verifiable order matching algorithm that is difficult to game by traders and operators alike. The proposed DEX is not: an escrow service, an arbitration service, or a platform for HFT.
While trades are fully trustless, steps are taken to discourage malicious clients from hindering normal trade activity. Registered clients are bound by the rules of community conduct, violation of which typically results in loss of trading privileges. Serious violations may result in a permanent ban, in which case the affected user must pay another registration fee to continue using the DEX.
The following list of non-negotiable, fundamental, DEX properties served as a basis for design.
- Non-custodial. Assets must be traded directly between clients, without the exchange being in control of assets at any time.
- Intermediary-free. Trades must be executed in a secure and trustless manner.
- Resistant to malicious or manipulative trading. Deliberately disruptive behavior should be disincentivized, and volume manipulation mitigated by pseudo-random epoch-based order matching.
Communication Protocols describes the messaging protocols and communication layer technologies that are to be used in for the DEX API.
Distributed Exchange Design Fundamentals describes the notable design aspects that facilitate an exchange service with the features described above.
Client Order Management details the different order types and the client/server workflows required to synchronize the order book and place orders.
Atomic Settlement walks through the settlement process with a couple of examples to provide a high-level overview.
Community Conduct describes the system of rules to which clients interacting with the DEX must adhere.
Trustless negotiation of trades requires considerable messaging. Transaction details must be reported and relayed at appropriate times, sometimes with substantial delays between subsequent actions. Match notification via HTTP polling or other request interval-based methods are thus not suitable for the DEX system. Persistent, full-duplex communication is critical to minimizing communication latencies and wasted bandwidth. Websockets ([4]) are chosen as the default and preferred communications protocol for the DEX exchange API. In addition to fulfilling the aforementioned needs, Websockets are now a well-established technology with client software available for integration in virtually all popular programming languages.
Websocket messages are secured by encryption on Transport Layer Security (TLS) [5] connections.
JavaScript Object Notation - Remote Procedure Call v2.0 (JSON-RPC 2.0) is a message formatting protocol for client-server communications [6]. The DEX API uses JSON-RPC exclusively for both inbound and outbound client communications. The JSON-RPC structure is simple and flexible.
{ "method": "string", "id": int, "params": {} || []; }
{ "result": {...} || null, "error": {...} || null, "id": int }
The method
is the "remote procedure" being called, analogous to
calling a function.
Most parameters are passed as part of a params
object or array.
Parameters will vary with method.
The id
field links a response to a request, and is optional for
notifications which require no response.
JSON-RPC 2.0 specifies that in the case of an error, the response's
error
field will be non-null
and the result
field will be
null
.
The opposite is true in the case of a success.
An API using HTTP for message transport may be provided for basic account management and server status queries, however websocket connections are to be the sole means for placing, monitoring, and executing orders. The primary reason for limiting the scope of the HTTP API is to eliminate client polling for rapidly changing resources, such as order status API endpoints.
There are several notable aspects of the DEX design that were chosen to permit peer-to-peer trades with the mechanics of order execution existing entirely on the separate blockchains via atomic swaps. These are:
- Asset-specific order quantity increments and transaction fee rates
- Epoch-based pseudorandom order matching
- Client identities based on public key infrastructure (PKI)
- An open and rigidly-defined interface for integration of arbitrary assets
There are a number of asset-specific variables that must be known by the client.
The lot size for an asset serves as both the minimum order quantity and the order quantity increment for limit orders and market buy orders, which are quantified in the market's base asset. In particular, for lot size l, the requested order quantity, Q, must satisfy
When the asset is the quote asset, a price increment is enforced. The rate, r, of a limit order must be an integer multiple of the price increment, p.
The DEX operator specifies an on-chain transaction fee rate (units atoms/byte) used when calculating the fees for initialization transactions.
Each asset will also have a minimum number of confirmations. This is the number of confirmations that are required for backing UTXOs as well as the number required during settlement on the first swap transaction before the second swap transaction is broadcast.
The minimum confirmation rule is waived for a change output from a transaction involved in a DEX-monitored trade.
The broadcast timeout is the amount of time a client has to broadcast a
transaction. For the maker, the broadcast time is measured from the time of
match notification.
For the taker, timeout is measured from the time when the maker's swap receives
its minconf
th confirmation.
Asset info should be requested by the user immediately after connecting.
JSON-RPC method: assets
, originator: client
field | type | description |
---|---|---|
epochlen | int | the epoch duration (seconds) |
buybuffer | float | the market buy buffer |
cancelmax | float | the cancellation threshold |
assets | [object] | list of Asset objects (definition below) |
btimeout | int | the broadcast timeout |
Asset object
field | type | description |
---|---|---|
symbol | string | ticker symbol |
lotsize | int | lot size (atoms) |
ratestep | int | the price rate increment (atoms) |
feerate | int | the fee rate for transactions (atoms/byte) |
swapsize | int | the size of the initialization transaction (bytes) |
minconf | int | minimum confirmations for backing UTXOs and swap transactions |
scripts | [string] | acceptable backing UTXO script types. minimum ["P2PKH"] |
The DEX collects no trading fees. Collecting fees from trades executed via atomic swaps (where the server is never in control of funds and settlement occurs directly on-chain) would add considerable complexity to the swap process and incentivize DEX operators to facilitate wash trading. Instead, a one-time fee is collected by the pool during registration. Registration fees discourage certain spam attacks, enable punitive actions when conduct rules are violated, and help to cover DEX operating expenses. Registration fees will be configurable by the exchange operator.
The clients will cover on-chain transaction fees at a minimum fee rate set by the DEX operator. Failure to provide the specified fee rate in a transaction will result in a conduct violation.
As part of any submitted order, a client is required to demonstrate control of funds that will back the atomic swap, and ensure that the backing funds are sufficient to create the swap contract transactions transferring the full order quantity as well as covering the network's transaction fees at the specified rate.
Total on-chain fees associated with an order will increase as the number of swap transactions required to settle the order increases. Maximum fees paid vary linearly with order size, but the actual fees realized can be significantly less than the maximum. See the atomic settlement section for examples of simple and complex matches and how that affects the swap transaction sizes.
Fee rates can vary greatly between assets. For many assets, low fees are possible without increasing the time until mined. Transaction fees tend to rise as an asset pushes the limits of on-chain scaling. For high-fee assets, the DEX operator must find a balance between lower fees, which are preferable from an accounting standpoint, and higher fees, which can decrease settlement time by increasing the speed at which a transactions are mined.
See also: Calculating Transaction Fees
In order to devalue predatory behavior exhibited by certain high-frequency trading algorithms, received orders are not processed continuously, but rather after a shuffling step with all other orders received in a fixed duration period called an epoch. The goal of this algorithm is to reduce the probability that an individual or collaborating pair can submit two orders that match with each other. Limiting this possibility mitigates advantages gained from front-running, spoofing, and other manipulative trading practices.
For a given epoch duration d > 0 , and current UNIX epoch time stamp t (in seconds since Jan 01 00:00:00 1970 UTC), the current order matching epoch index, i, and epoch range are computed as
where / is integer division. For example, at the time of writing, t = 1562008475 , which for d = 60 corresponds to epoch number i = 26033474 spanning [1562008440, 1562008500). This convention allows epoch times to be known without querying the server.
A clock synchronization protocol such as NTP will be used to ensure server and client clocks are synchronized within acceptable tolerances.
When the epoch ends, a match cycle begins.
First, the order pool is shuffled. Shuffling is deterministic, using the Fisher-Yates algorithm where the random number generator seed is derived from the hash of the concatenated order ID hashes. Specifically for hash function f, the seed hash, Hseed is computed as
where orderi represents the order ID of the ith order in a lexicographically sorted list of order IDs and || indicates concatenation.
The order IDs and final hash are published at the start of the matching process. This is done so that the shuffling may be independently computed for external verification and to facilitate server mesh operation.
Orders are processed one at a time. Each order is matched according to its type.
1. If the order is a cancel order, any corresponding standing limit order is removed from the list and the cancel order is considered filled. If a cancellation is processed before the order that it cancels, the cancellation will fail, and will need to be re-submitted. That is, cancel orders do not affect down-queue orders, only standing orders.
2. If the order is a limit order with time in force standing that cannot match immediately (a maker), it is added to the standing orders. It is immediately able to match orders further down the queue.
3. If the order is a taker, it is matched against the best available standing order. Clients for both orders are notified and the settlement process begins. The orders are set aside for monitoring. If a limit order with time in force standing on either side of the match is only partially filled, it is added to the standing orders with the appropriate modifications and is immediately available for matching again.
Any unmatched quantity on a limit order with time in force immediate is left unfilled. Market orders and immediate limit orders cannot match orders further down the queue.
When a limit order matches a limit order, the price assigned to the match is the average of the two orders, with a split atom going to the maker.
The process continues with the next order in the list and iterates until all orders have been processed.
The server and the clients are identified and authenticated using public keys, with matching private keys used to sign and authorize orders and other messages. Establishing client identity with public keys keeps the notion of client identity to a minimum, while providing a number of other security benefits throughout the order placement and execution processes.
All data submitted to the exchange server from a client must be signed with the client's private key and authenticated by the server using the corresponding public key, so using client public keys directly for identity purposes is a natural simplification that obviates the need for client user names and passwords.
Further, since Politeia, Decred's governance platform, also employs a PKI, the same identities may be used on both services to facilitate time-stamping exchange data via Politeia. For example, given common identities between the DEX and Politeia, anchoring data related to DEX client and server conduct on the Decred blockchain may be useful for establishing a reputation system.
Clients need atomic-swap enabled wallets and the ability to broadcast transactions to each of the blockchain networks involved in the swap.
DEX operators need access to trusted full nodes for each of the assets supported. While operation via a surrogate blockchain data service such as a block explorer is potentially feasible, it would entail significant security risks. Initial development will require a direct connection to full nodes of each asset's blockchain.
Adding support for an asset is accomplished by implementing a particular
Go interface.
The interface is exported and compiled with -buildmode=plugin
to enable
dynamic importing by the exchange operator.
The operator will decide which plugins to import via configuration file.
As the expected interface is expected to evolve greatly during initial
implementation, the specifics of interface structure will be released at a later
time.
With the exception of Decred and Bitcoin, which will be implemented during initial phases of DEX development, it is expected that development communities will release their own appropriately vetted plugins.
An account is uniquely identified by the client's public account key (pubkey),
which the client provides during registration.
After registration, the client does identify themselves with their pubkey
directly.
Instead, the account is identified by an account ID, which is the
double SHA-256 hash of the client's pubkey,
sha256(sha256(pubkey))
, provided as a hex-encoded
string in JSON-RPC calls.
The user creates a websocket connection and sends their public account key. The message is signed with the corresponding private account key. The response includes the server's public key. The server's public key will also be pre-published for further validation.
JSON-RPC method: register
, originator: client
field | type | description |
---|---|---|
pubkey | string | hex-encoded public account key |
timestamp | int | UTC timestamp |
sig | string | hex-encoded signature of serialized registration. serialization described below |
Registration serialization
field | size (bytes) | description |
---|---|---|
pubkey | 32 | the user's public account key |
timestamp | 8 | the client's UNIX timestamp |
Response parameters
field | type | description |
---|---|---|
pubkey | string | hex-encoded server public key. |
address | string | fee address |
fee | int | fee (atoms) |
timestamp | int | UNIX timestamp |
sig | string | hex-encoded signature of the serialized response. serialization described below |
Response serialization
field | size (bytes) | description |
---|---|---|
DEX pubkey | 32 | the server's public key |
client pubkey | 32 | the client's public account key |
timestamp | 8 | the server's UNIX timestamp |
fee | 8 | registration fee (atoms) |
address | varies | ASCII-encoded fee address |
The client pays the fee on-chain and notifies the DEX of the transaction detail. The fee is paid with a standard P2PKH output to the address received in step 1. The server immediately sends their receipt and then closes the connection.
JSON-RPC method: notifyfee
, originator: client
field | type | description |
---|---|---|
accountid | string | client's account ID |
txid | string | transaction ID |
vout | int | the transaction output index which pays the fee |
timestamp | int | UNIX timestamp |
sig | string | hex-encoded signature of serialized fee notification. serialization described below |
Notification serialization
field | size (bytes) | description |
---|---|---|
account ID | 16 | client account ID |
tx ID | 32 | the transaction ID |
vout | 4 | transaction output index |
Response parameters
field | type | description |
---|---|---|
sig | string | server hex-encoded signature of client's serialized notification |
Once the transaction is mined and has the
requisite number of confirmations, the client
should create a new authenticated connection.
If the client attempts to connect before minconf
confirmations, the
DEX should reject the connection outright.
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.
Orders must be placed on an authenticated connection. Once a websocket connection is established, the client will supply their account ID and signature.
JSON-RPC method: connect
, originator: client
field | type | description |
---|---|---|
accountid | string | account ID |
apiver | int | requested API version |
timestamp | int | UNIX timestamp |
sig | string | hex-encoded signature of serialized connection data. serialization described below |
Connect serialization
field | size (bytes) | description |
---|---|---|
account ID | 16 | client account ID |
API version | 2 | requested API version |
timestamp | 8 | the client's UNIX timestamp |
Connect response
If the client connects shortly after a trade suspension, it's possible that trading will not commence until a future epoch. The DEX response will indicate when trade did or will begin.
If a client unexpectedly disconnects with active orders, the orders may match in the client's absence. A list of any pending matches is included in the response.
field | type | description |
---|---|---|
startepoch | int | the epoch at which trading did or will commence. May be in the future e.g. after maintenance |
matches | [object] | list of Match objects |
If a connection is lost after placing an order, the client must reconnect and complete settlement on any matches made in their absence. Regardless of connection status, once a match is made, a client is always subject to violation of rule 1 via the broadcast timeout. This is true regardless of connection status.
If a client is disconnected for a full epoch duration, any standing limit orders may be auto-cancelled by the server. These cancellations will count toward the client's cancellation ratio, so could potentially result in penalties. The client should manage their orders carefully to ensure that this doesn't occur.
Market orders and limit orders with time in force immediate are not cancelled under disconnection. Once submitted, these orders will always go through a single match cycle.
If a client is in the settlement phase when a connection is dropped, the client will be expected to reconnect and complete settlement.
An order book can be viewed and tracked by subscribing to a market.
JSON-RPC method: orderbook
, originator: client
field | type | description |
---|---|---|
base | string | currency code for the market's base asset |
quote | string | currency code for the market's quote asset |
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
field | type | description |
---|---|---|
book | object | An OrderBook object |
seq | int | A sequence ID |
JSON OrderBook object
field | type | description |
---|---|---|
marketid | int | the market ID. included with order book updates. |
epoch | int | the current epoch |
orders | [object] | A list of Order objects |
JSON Order object
field | type | description |
---|---|---|
oid | string | the order ID |
otype | string | "l" for limit, "m" for market |
side | string | "b" for buy, "s" for sell |
osize | int | order size (atoms) |
rate | int | price rate (quote asset per unit base asset, atoms). only set on limit orders |
tif | string | time in force. one of "i" for immediate or "s" for standing. only set on limit orders |
epoch | int | the epoch in which the order was received |
Changes to the orderbook will be received from the DEX as a stream of
updates.
These updates take the JSON-RPC notification format, so omit an id
attribute.
JSON-RPC method: remove_order
, originator: DEX
field | type | description |
---|---|---|
marketid | int | the market ID |
orderid | string | hex-encoded order ID |
seq | int | A sequence ID |
JSON-RPC method: add_limit
& add_market
, originator: DEX
field | type | description |
---|---|---|
marketid | int | the market ID |
order | object | the Order object |
seq | int | A sequence ID |
At the beginning of the matching cycle, the DEX will publish a list of order IDs and the seed hash used for order sequencing.
JSON-RPC method: match_data
, originator: DEX
field | type | description |
---|---|---|
marketid | int | the market ID |
epoch | int | the epoch for which the cycle occurs |
orderids | [string] | list of order IDs for the epoch |
seed | string | hex-encoded sorting key |
A client can unsubscribe from order book updates without closing the websocket connection.
JSON-RPC method: unsub_orderbook
, originator: client
field | type | description |
---|---|---|
marketid | int | the market ID |
As part of the order, the client must demonstrate control of funds. This is accomplished by supplying information and a signature for each previous output (UTXO) that will be spent. The client covers the backing fees associated with the inputs spending their own UTXOs.
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 an initialization transaction with 1 standard P2PKH input, 1 standard P2PKH output, and 1 swap output.
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 UTXOs, futxo. The client must know how to calculate the script sizes to assess fees. The DEX will verify the UTXO sum before accepting the order.
All backing UTXOs must have a minimum number of confirmations. The exact number is set by the DEX operator.
With the exception of market buy orders, which are detailed below, for an order of quantity Q, the sum value of the selected UTXOs, V, must satisfy the relation
There may be types of pubkey script which are not implemented. As a rule, P2PKH will always be available.
As part of the order, the client will submit a list of UTXO objects.
JSON UTXO object
field | type | description |
---|---|---|
txid | string | hex-encoded transaction ID |
vout | int | the transaction output index |
pubkey | string | hex-encoded public key |
sig | string | hex-encoded signature of serialized UTXO data. serialization described below |
extradata | string | hex-encoded data. the script for P2SH. empty for P2PKH |
The sig
is a signature of the UTXO data serialized according to the
table below. The data is signed with the private key corresponding to
pubkey
.
The pubkey
must hash to a receiving address in the UTXO's pubkey
script.
UTXO serialization
field | size (bytes) | description |
---|---|---|
tx hash | 32 | the transaction hash |
vout | 4 | the UTXO's transaction output index |
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 a common prefix structure.
Prefix serialization
field | size (bytes) | description |
---|---|---|
account ID | 16 | client account ID |
base asset | 5 | the base asset for the market. ASCII-encoded ticker symbol, with leading zeros for 3- and 4-letter tickers |
quote asset | 5 | the quote asset for the market. ASCII-encoded ticker symbol, with leading zeros for 3- and 4-letter tickers |
order type | 1 | the type of order. limit = 0, market=2, cancel=3 |
client time | 8 | the client's UNIX timestamp |
server time | 8 | the server's UNIX timestamp. zeros for client-size serialization |
The order serialization is used to create a unique order ID. The ID is defined as the SHA-256 hash of the serialized order. It is represented in messaging as a hex-encoded string.
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 cancelled, 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.
JSON-RPC method: limit
, originator: client
field | type | description |
---|---|---|
accountid | string | client's account ID |
timestamp | int | UTC timestamp generated at the time of order submission of trades requires a fair amount of messaging between the 2 clients, via the DEX trade API. |
tradepair | string | market. trading pair. e.g. DCR/BTC |
ordertype | string | "limit" for a limit order |
side | string | one of "buy" or "sell" |
ordersize | int | order size (atoms) |
rate | int | price rate (quote asset per unit base asset, atoms) |
timeinforce | string | one of standing or immediate |
utxos | [UTXO] | list of backing UTXOs |
orderid | string | hex-encoded order ID |
sig | string | client hex-encoded signature of the serialized order data. serialization described below |
address | string | address where the matched client will send funds |
Response parameters
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 |
Limit order serialization
field | size (bytes) | description |
---|---|---|
prefix | 65 | the order prefix |
UTXO count | 1 | The number of backing UTXOs |
UTXO data | 36 x count | sequence of UTXO data |
side | 1 | 0 for buy, 1 for sell |
rate | 8 | price rate |
quantity | 8 | quanity to buy or sell (atoms) |
time in force | 1 | 0 for standing, 1 for immediate |
address | varies | client's receiving address |
A market order is an order to buy or sell an asset at the best available
market price. The JSON-RPC params
fields are similar to a limit
order, but without the rate
field.
Market orders cannot be cancelled. Any portion of the requested quantity that does not match immediately (during the epoch match cycle) is left unfilled.
JSON-RPC method: market
, originator: client
field | type | description |
---|---|---|
accountid | string | client's account ID |
timestamp | int | UTC timestamp. order broadcast time |
tradepair | string | market. trading pair. e.g. DCR/BTC |
ordertype | string | "market" for a market order |
side | string | one of "buy" or "sell" |
ordersize | int | order size, in atoms (*1e8). See notes on market buy orders below. |
utxos | [UTXO] | list of backing UTXOs |
orderid | string | hex-encoded order ID |
sig | string | client hex-encoded signature of the serialized order data. serialization described below |
address | string | address where the matched client will send funds |
Response parameters
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 |
Market order serialization
field | size (bytes) | description |
---|---|---|
prefix | 65 | the order prefix |
UTXO count | 1 | The number of backing UTXOs |
UTXO data | 36 x count | sequence of UTXO data |
side | 1 | 0 for buy, 1 for sell |
quantity | 8 | quantity to buy or sell (atoms) |
time in force | 1 | 0 for standing, 1 for immediate |
address | varies | client's receiving address |
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 prevent orders becoming invalid (too small to match) due to a price increase,
the DEX operator chooses a market buy buffer, bm > 1.
For a market with 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.
JSON-RPC method: cancel
, originator: client
field | type | description |
---|---|---|
accountid | string | client's account ID |
timestamp | int | UTC timestamp. order broadcast time |
tradepair | string | market. trading pair. e.g. DCR/BTC |
orderid | string | hex-encoded order ID |
sig | string | client hex-encoded signature of the serialized order data. serialization described below |
Response parameters
field | type | description |
---|---|---|
sig | string | server hex-encoded signature of the serialize order data, after adding the DEX timestamp |
server time | int | the server's UNIX timestamp |
Cancel order serialization
field | size (bytes) | description |
---|---|---|
prefix | 65 | the order prefix |
orderid | 16 | the order ID |
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.
JSON-RPC method: match
, originator: DEX
field | type | description |
---|---|---|
orderid | string | order ID |
matchid | int | the match ID to use for progress notifications |
quantity | int | the matched amount, in atoms of the base asset |
rate | int | the rate that the order matched at (as quote asset per unit base asset, atoms) |
address | string | the client's receiving address |
timestamp | int | UTC timestamp. order broadcast time |
sig | string | server hex-encoded signature of the serialized notification data. serialization described below |
The client will be responsible for serializing the notification and checking the signature. In turn, the client should themselves sign the notification data and return an acknowledgement to the DEX.
Client Acknowledgement
The client will return a list of signed match acknowledgements.
field | type | description |
---|---|---|
matchid | int | the match ID |
sig | string | client hex-encoded signature of the serialized notification data. |
Match notification serialization
field | size (bytes) | description |
---|---|---|
orderid | 32 | the order ID |
matchid | 8 | the ID assigned to this match |
quantity | 8 | the matched amount, in atoms of the base asset |
rate | 8 | the rate that the order matched at |
address | varies | ASCII encoded receiving address for the match |
After the client broadcasts both their initialization and redemption transactions, they are expected to report the transaction details to the server for verification and relay to the counterparty.
JSON-RPC method: progress
, originator: client
field | type | description |
---|---|---|
swap stage | string | one of "init" or "redeem" |
matchid | int | the matchid, retrieved from the match notification |
txid | string | the hex-encoded transaction ID |
vout | int | the output index |
timestamp | int | UTC timestamp. order broadcast time |
sig | string | client signature of the serialized progress notification. serialization described below |
DEX Acknowledgement
field | type | description |
---|---|---|
matchid | int | the match ID |
swap stage | string | one of "init" or "redeem" |
sig | string | client hex-encoded signature of the serialized progress notification. serialization described below |
Progress notification serialization
field | size (bytes) | description |
---|---|---|
orderid | 16 | the order ID |
matchid | 8 | the ID assigned to this match |
swap stage | 1 | 0 for initialization transaction, 1 for redemption |
tx hash | 32 | the transaction hash |
vout | 4 | the output index |
timestamp | 8 | the client's UNIX timestamp |
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 orderbook in any form.
JSON-RPC method: revoke_match
, originator: DEX
field | type | description |
---|---|---|
orderid | string | order ID |
matchid | int | the match ID |
sig | string | server hex-encoded signature of serialized revocation. serialization described below |
Revocation serialization
field | size (bytes) | description |
---|---|---|
orderid | 32 | the order ID |
matchid | 8 | the ID assigned to this match |
There are a number of scenarios where the server may suspend operations, intentionally or not. During trade suspension, standing limit 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. The epoch specified in the suspension notification is the first epoch for which orders will no longer be accepted. Users should expect to lose connection during suspension. When possible, the server should settle all existing matches before closing connections. If that is not possible, 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 startepoch.
JSON-RPC method: suspension
, originator: DEX
field | type | description |
---|---|---|
epoch | int | the epoch in which the suspension will start |
persist | bool |
In order to facilitate trustless, non-custodial exchange, the DEX leverages an atomic swap process that enables all parties to maintain full control over their assets at every step. The DEX primarily acts as a matchmaker, connecting two orders and relaying messages between the two clients. Additionally, the DEX performs some trade monitoring to ensure that parties are acting in good faith. Clients who fail to act on their trades appropriately will be issued a penalty in accordance with the rules of community conduct.
The following cases are meant to provide a high-level overview of the trade process, and to illustrate how to handle a perfect match, a partial fill, and multiple matches within an epoch. For the sake of illustration, the following simplifications are made.
- The lot size for the market is 1 DCR.
- All backing funds for the orders are in the form of a single UTXO.
- There are no fees.
In the most basic case, Alice and a trader named Bob are the only participants and their orders match perfectly.
A1: Alice places a limit order to sell 3 DCR at 0.1 BTC per DCR for a total of 0.3 BTC. As a limit order, Alice must specify both the quantity and the price, as a rate, as well as her BTC address. Alice signs the order information. The signature provides proof of the order and justifies any penalties imposed on Alice should she fail to follow through on a trade.
A2: During some later epoch, Bob places a market order to buy DCR with BTC at the best available price. Bob’s order will contain both an address and a signature. As a market order, Bob’s order must match in this epoch. Any unfilled portion is immediately cancelled.
A3: At the end of the epoch, the DEX performs a match cycle, recognizes the match, and informs both Alice and Bob that a match has occurred. Because the order quantities are identical, the orders are simply removed from the book and set aside for monitoring. The DEX informs both clients that a match has been made and provides Bob with Alice’s address.
A4: Because Alice is the maker, she will broadcast her initialization transaction first. She will be expected to broadcast and report her transaction to the DEX within a specified amount of time.
Before Alice can prepare her initialization transaction, she must generate a key known only to herself. From the key, Alice generates a “lock” and constructs an output script such that if someone can provide both Alice’s key and the pubkey for Bob’s specified address, they can spend the output. In practice, the key is simply a random 32-byte number, and the lock is its hash.
In addition, Alice constructs the output script with a second, alternative lock that allows her to spend the output herself, but only if the output remains unspent for a specified amount of time. Alice sets her timelock to 48 hours.
Alice broadcasts her initialization transaction to the Decred network. She informs the DEX of the transaction details, which the DEX relays to Bob. Alice does not reveal the key that she created.
A5: Bob inspects Alice's transaction and retrieves the lock. After the requisite number of confirmations, Bob prepares his initialization transaction. He uses Alice's lock here as well, and creates an output script satisfied by Alice’s key and the pubkey for Alice’s address.
Bob sets his timelock to 24 hours. Bob should also check that the timelock in Alice's initialization is set to 48 hours. If Alice’s timelock duration is set near or below Bob’s timelock duration, Alice could potentially spend the DCR output before Bob.
Bob broadcasts his initialization transaction to the Bitcoin network. He informs the DEX of the transaction details, and the DEX relays the information to Alice.
A6: Alice prepares a redemption transaction that spends the output from Bob’s initialization transaction to her own address. In order to spend the Bitcoin from Bob’s transaction, she needs to provide the key created in step 4.
Alice broadcasts her redemption transaction to the Bitcoin network. Though both the DEX and Bob are capable of monitoring for the transaction, it is expected that Alice informs the DEX server of the transaction details anyway. The DEX server will notify Bob of the details.
A7: Bob prepares his redemption transaction using the key gleaned from Alice’s redemption transaction and the output from Alice’s initialization transaction.
Bob broadcasts his redemption transaction to the Decred network, completing the atomic swap.
In case B, Alice is similarly trying to sell 3 DCR for 0.3 BTC, but the match found by the DEX is not perfect this time.
B1: Same as A1. Alice submits her signed order to the DEX.
B2: Bob places a market order for 0.1 BTC worth of DCR at market price. A new client, Carl, places an identical market order for 0.1 BTC worth of DCR.
B3: The DEX finds the match and informs all parties, providing Alice with Bob and Carl's addresses. The DEX cannot simply remove Alice’s order, because it is only partially filled. In this case, the DEX does not remove the order from the order book, instead just marking it partially filled and calculating the remaining quantity. The order is returned to the order book and is immediately able to match again.
B4: As the maker, Alice goes first again. She groups her matches from the epoch and creates two different keys, one for Bob and one for Carl. She sets her timelocks to 48 hours. Alice broadcasts her initialization transaction to the Decred network and informs the DEX about the transaction details, which the DEX relays to Bob and Carl.
B5 After waiting the requisite number of confirmations, Bob and Carl prepare their initialization transactions using the locks from Alice's transaction. They broadcast their transactions.
B6 and B7 follow naturally from case A, with Alice creating two redemption transactions, one for Bob and one for Carl. Bob and Carl then follow up with their own redemptions.
By registering, clients agree to abide by the rules described here. These rules are designed to ensure that clients are acting in good faith and maintaining behaviors that contribute to a smooth DEX experience for humans.
Swap transactions must be created at the correct times (see broadcast timeout).
In the event that the maker fails to start the atomic swap process with their initialization transaction, the taker will be notified that order execution is terminated due to failure of the taker to accept the order. The Maker's limit order will not go back on the order book, but they may be given the option to replace the order.
In the event that the taker fails to respond to the maker's initialization transaction, the maker will incur no violation.
The cancellation ratio is the ratio of the count of cancelled orders to the count of completed orders. The cancellation threshold is set by the DEX operator. An order is considered completed when all matches have fully settled. An order is considered cancelled when a cancel order is matched to a standing limit order. The server may also cancel an order if the client's connection is dropped and the client fails to reconnect for more than 1 epoch duration. Cancellation of a partially filled order is counted as a full cancellation. The cancellation ratio is evaluated on a 25-order rolling window.
The swap output value must be sized to exactly the matched amount. The fee rate must be at least the minimum value set by the DEX. It is the client's responsibility to ensure that fees on a partial fill are not overpaid to a level that results in a violation of rules 1 or 3 when the remaining portion is matched.
The primary penalty for breeches of conduct is a ban, which includes loss of trading privileges, forfeiture of registration fee, and immediate revocation of any unfilled orders.
Less drastic punitive measures such as a cool-down period may be considered for minor, first-time or infrequent conduct violations.
Trade history will be made available to both websocket and HTTP clients.
Operation of the DEX will require substantial administrative labor. High throughput and low latency networking and high performance hardware are expected requirements. Historical trade data should be archived for some reasonable amount of time, if not forever. Clients will expect near-perfect uptime and long-term operation.
Additionally, operators will need to manage exchange settings properly to keep markets liquid and to balance fees and settlement speed. That said, changes to exchange or asset variables will often entail server revocation of all existing orders on a market, so should be done as infrequently as possible.
Exchange Variables
variable | relevant section | units | default |
---|---|---|---|
epoch duration | Epoch-based Order Matching | seconds | 60 |
market buy buffer | Market Buy Orders | unitless ratio | 1.25 |
registration fees | Fees | atoms | 1e8 (1 DCR) |
cancellation threshold | Rule 2 | unitless ratio | 0.6 |
Per-asset Variables
variable | units | description |
---|---|---|
lot size | atoms | The minimum order quantity and the order quantity increment when an asset is the base asset. |
rate step | atoms | The minimum price rate and the price rate increment when an asset is the quote asset. |
fee rate | atoms/byte | The minimum fee rate for swap transactions. |
minimum confirmations | count | The Minimum confirmations for backing UTXOs and swap transactions. |
See also Exchange Variables.
...
[1] Teall, John L., Financial Trading and Investing, p. 336 (2013)
[2] Bitwise Presentation to the U.S. Securities and Exchange Commission (2019)
[3] Atomic Swaps