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, with epoch duration configured per-market
- Client identities based on public key infrastructure (PKI)
- An open and rigidly-defined interface for integration of arbitrary assets
There are a number of exchange-wide, market-specific, and asset-specific
variables that must be known by the client. These settings should be requested
by the user immediately after connecting via the
config
request.
The cancellation ratio for a user is the ratio of their canceled order count to
completed order count. A user's cancellation ratio must remain below the configured
cancellation threshold
(cancelmax
) to avoid penalty.
The broadcast timeout (btimeout
) 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
swapconf
th confirmation (see Asset Variables).
The user's transaction that pays the registration fee specified by the
register
response during account creation
must have regfeeconfirms
confirmations before the server will
accept the user's fee payment notification
and complete the registration process.
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
Every asset is assigned a unique integer ID that will be used to identify the asset in serialized structures. Whenever possible, the ID is the same as the BIP-0044 registered coin type index [6].
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.
The swapconf
is the number of confirmations required during
settlement on the first swap transaction, before the second swap transaction is
broadcast, as well as on the second swap transaction, before the first
redemption is broadcast.
If the client connects shortly after a
trade suspension, it's possible that
trading will not commence until a future epoch. The DEX's config
response will indicate for each market at which epoch index trading did or will
begin (startepoch
), along with the epoch duration in milliseconds
(epochlen
), for each market.
The market buy buffer, which defines the
minimum order size for a market buy order, is also in the market configuration
returned in the server's response as buybuffer
.
Request route: config
, originator: client
The config
request payload
can be null. The DEX will
respond with its current configuration.
result
field | type | description |
---|---|---|
cancelmax | float | the cancellation threshold |
btimeout | int | the broadcast timeout |
fee | int | registration fee (Decred atoms) |
regfeeconfirms | int | required confirmations for the registration fee payment transaction |
assets | [object] | list of Asset objects (definition below) |
markets | [object] | list of Market objects (definition below) |
Asset object
field | type | description |
---|---|---|
symbol | string | ticker symbol |
id | int | a unique per-asset ID |
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) |
swapconf | int | minimum confirmations for swap transactions |
Market object
field | type | description |
---|---|---|
name | string | market name |
base | int | base asset ID |
quote | int | quote asset ID |
epochlen | int | the epoch duration (milliseconds) |
buybuffer | float | the market buy buffer |
startepoch | int | the epoch number at which trading did or will commence. May be in the future e.g. after maintenance |
finalepoch | int | the epoch number at which trading will be suspended. Only present when a suspension is scheduled |
persistbook | bool | whether or not booked orders will be persisted through a scheduled suspension. Only present when a suspension is scheduled |
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 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 ensure that an individual client cannot obtain a deterministic latency advantage over other clients when executing trades. Limiting this possibility mitigates advantages gained from front-running, spoofing, and other manipulative trading practices.
For a given epoch duration d > 0 in milliseconds, and current UNIX epoch timestamp t (in milliseconds 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 = 1562008475123 , which for d = 8000 (8 seconds) corresponds to epoch number i = 195251059 spanning [1562008472000, 1562008480000). This convention allows epoch start and end times for any epoch duration 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. The preimage for each order is collected and the orders are sorted lexicographically by their order IDs.
A random seed is derived from the Blake-256 hash of the concatenated order preimages.
where || indicates concatenation and pi is the preimage of the ith order.
The integer series produced by evaluating the hash as a series of 8-byte big-endian integers is used to seed a Mersenne Twister (mt19937) random number generator.
Shuffling is then performed using the The Fisher-Yates algorithm on the N orders, where at each step i : i < N, the order at index i is swapped with the order at index j,
ri is the ith number in the seeded mt19937 series.
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 from the queue matches a standing limit order on the book, the match is assigned the price rate of the maker's order (the standing order's price rate).
The process continues with the next order in the list and iterates until all orders have been processed.
The preimages and seed are published at the start of the matching process. In addition, a checksum, which is the Blake-256 hash of the lexicographically sorted order commitments, is sent to the client. Because the client already has the commitments from their order book subscription, the checksum allows the client to check that the orders underlying the preimages are indeed the same set received in their order notification feed.
For the special case of an epoch with no orders, the checksum will be null.
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 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 wallets that support atomic swaps 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 that the server have a direct connection to full nodes of each asset's blockchain.
Adding support for an asset is accomplished by writing a Go package with types that implement a particular set of interfaces, defined here and here. There are then two ways to import the asset backend into the server software.
- The package is compiled with
-buildmode=plugin
and imported at runtime by specifying the plugin in the server configuration. - The backend is added to the dcrdex repository, and imported directly at compile time.