Exchange Architecture

Learn the technology behind Crystal's next-generation on-chain order book exchange.

All markets (e.g. WETH/USDC) are accessible through a single central exchange contract and cannot not be interacted with otherwise. To get the address of each pair, query getPair on the exchange contract. The central exchange contract is the only relevant entrypoint to the core protocol and contains router endpoints designed for ease of use and lower level exchange endpoints designed for greater versatility. It is recommended that the router endpoints (e.g. swapExactTokensForTokens) on the exchange contract are used when directly interacting with Crystal, as endpoints such as marketOrder are intended for market makers and DeFi integrations.

The exchange contract supports the native token of the blockchain, such as MON for Monad, through the router endpoints. It also supports read functions to calculate input/output amount. The core write methods are designed to resemble the Uniswap V2 router for familiarity while also allowing for multihop swaps (e.g. WETH to USDC to WBTC), with the only difference being the addition of an optional referral code.

The router also provides an additional swap function to leverage the capabilities of Crystal's order book that most AMMs lack, which are:

  • Orders that partially fill up to a set price.

  • Market-to-limit orders that fill up to a set price and then set a limit order with the remaining unfilled portion.

The router also has functions for placing and canceling limit orders, with support for native tokens as well. Finally, the router also allows for order batching across multiple markets in a single transaction, claiming fees from all markets in a single transaction, and trades that automatically route across the launchpad and its graduated markets.

Crystal supports a single privileged party, which can be queried at any time with the gov function. Anyone can create new markets with parameters set to their desire, but the privileged party is able to elevate other addresses to become canonical deployers, allowing them to deploy canonical markets. This process is done to ensure end users only see markets configured with reasonable parameters, and over time will be decentralized to protocol governance. Additionally, select market parameters can be altered by this privileged party, but these changes cannot affect existing orders or move user funds. The fee, fee address, and minimum size can be changed by the market creator in a canonical market, while other parameters can be changed by creating a new market with the same tokens. In this case, the new market will overwrite the former pair in the router's token addresses to market address mapping. The old market will continue to function as normal, but it is recommended to use the latest created market as the router will automatically use the new market. Parameters will only be changed rarely by protocol governance and with reason. By default, interactions with the router will use the latest created market, so this is not an issue for end users to worry about. Tokens are only approved once to the exchange contract, individual markets do not need to be seperately approved. Importantly, neither a misconfigured market nor the privileged party can tamper with user funds either from existing orders or through approvals.

Each market is between a quote asset and base asset, and the central exchange contract contains read functions to query relevant parameters the current state of the order book, individual price levels and orders, as well as estimated input/output quotes for trades. There are also methods to batch these queries, designed for use by frontends and off-chain interfaces. Some of these values may be packed for efficiency, and further information can be found in the Smart Contract Methods section. Buy orders are defined as exchanging the quote asset for the base asset, while sell orders are defined as exchanging the base asset for the quote asset. Buy orders are denominated in terms of the quote asset, while sell orders are denominated in the base asset for both market and limit orders. If the output amount is specified rather than the input amount, the converse is true. Price is always defined as price = (quote asset amount * scale factor / base asset amount), with scale factor being a parameter used to allow for decimal precision. For example, for a market WETH/USDC with a scale factor of 10**13 where WETH has 18 decimals and USDC has 6 decimals, a price of 4123.4 USDC per WETH would be represented as 41234.

At the core of each market are the limit orders, which collectively make up the order book. Limit orders are constrained by:

  • a minimum size

  • a maximum price

  • the bid-ask spread

The first two constraints are to mitigate spam and lower gas for all users. To place a limit order with the potential of crossing the bid-ask spread, ensure that add liquidity only is disabled to place a market-to-limit order which will fill as a taker order up until your limit price if necessary.

Within each price level, limit orders are stored in a doubly linked list, and orders are filled on a first-in first-out basis. Each order receives a unique ID and is mapped (ID -> linked list node). Placing an order simply appends it to the linked list, while cancelling removes the corresponding ID -> node pair from the hashmap and reconnects its neighboring nodes, thus providing O(1) operations for both placement and cancellation of orders. Price levels are stored as a sparse array from 0 up until the maximum price. A bitmap marking price levels with active orders is used to traverse across these price levels in near-constant compute. When the size of a limit order is either queried or emitted, the resulting value is denominated in the quote asset in the case of a buy order and base asset in the case of a sell order. Limit orders are either feeless or recieve a fee rebate when filled, importantly they never pay a fee and are guaranteed to execute at the specified price or better.

Limit orders can be canceled based on their price and unique id if they have not yet been filled. Placement and cancelation of limit orders are O(1) operations, and each order and price level is packed into a singular storage slot. For market makers, the calldata necessary to place or cancel an order is a single 32-byte chunk, and multiple of these operations can be batched into a single transaction. Both placement and cancelation of limit orders will emit events containing the order's owner, id, price, and size. Limit orders placed and canceled in batches will emit batched event logs as the indexed owner parameter can be reused, while the data of each individual order follows in 32-byte chunks. Individual fills are emitted seperately and can be subscribed to through an indexed owner parameter. Market makers also have the option and are recommended to specify a client order id for a variety of reasons including easier indexing, cheaper gas, and the ability to cancel inflight orders. In this case, the emitted id consists of the owner and client order id which is irrespective of market or price level. Importantly, client order ids can and should be reused once canceled or filled, but must be unique if the previous order is active.

Market orders match the existing order book and are executed immediately. They fill orders up until their designated worst acceptable price, at which they will either terminate, revert, or transform into a limit order with the remaining portion depending on the order type. Either the desired input or output amount can be specified, with denominations in their respective tokens. Tokens are directly transferred from the address placing the market order to the owner of each limit order filled, therefore requiring token approval. The router handles this automatically when swapping with native tokens. Market orders emit an event containing the address, amount in, amount out, last filled price, and a list of all limit orders filled. A fixed fee is charged, with the potential for a referral commission and/or creator commission. This fee is modifiable on market deployment and can be altered by governance, but is unlikely to frequently change once a market is deployed. There is a fixed maximum fee of 10% to prevent malicious attacks in the unlikely event the governance address becomes compromised. Protocols integrating Crystal's liquidity are encouraged to apply their referral address in the calldata of their transaction.

All exchange functionality occurs fully on-chain, including the order book, matching engine, and necessary data for both market makers and end-user interfaces. Besides parameters necessary to ensure optimal performance, all deployed contracts are both permissionless and immutable. For additional questions, please reach out through our Discord.

Last updated