Exchange Architecture

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

Each market (e.g. WETH/USDC) is deployed as a separate smart contract by a factory contract. To get the address of each pair, query getPair on the router contract. The router serves as an endpoint to interact with all the other contracts, including creating pairs and performing trades through each market. Individual markets can also be interacted with, but it is recommended that end users use the router, as directly interacting with individual pairs is intended for market makers and DeFi integrations.

The router supports the native token of the blockchain, such as MON for Monad. The router 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 has a database containing a map of all the referral codes to addresses and vice versa.

The router supports a single privileged party, which can be queried at any time with the gov function. This address is given the option to create new markets with parameters set to their desire, as well as set a privileged address to each market where protocol fees are sent. Fee addresses and 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 maximum price can be changed by the existing fee address, 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, such as if an asset's price comes close to exceeding the set maximum. 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 approved to each market separately, and the router neither stores balances nor requires approvals. The privileged party has no way of taking user funds either from existing orders or through approvals.

Each market is between a quote asset and base asset, and 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. To save a storage slot, each user is required to register once before placing limit orders, effectively transforming their address into a unique uint32. For end users, the router does this automatically, so it only applies to market makers or users interacting through third party integrations, although this should be seamlessly managed by the integration and not the user.

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 use 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. When the size of a limit order is either queried or emitted, the resulting value is equal to the quote asset amount multiplied by the scale factor. Limit orders are not charged any fee.

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.

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 0.03% fee is charged, with the potential for a 25% referral commission and 25% referral rebate (both are calculated using the initial fee amount for an effective protocol fee of 0.015% per swap). This fee is modifiable on market deployment and can be altered by governance, but will remain 0.03% for any non-stable markets and 0.01% for any stable markets listed in the foreseeable future. There is a fixed maximum fee of 5% 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 through their smart contracts/frontend.

All exchange functionality occurs fully on-chain, including the order book, matching engine, and necessary data for 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