Hydro Relayers contain several key components to effectively gather, process, and settle transactions. This section details each component of the general relayer stack.
- Component Chart
This is the main entry point for traders to interact with the Hydro Relayer. The web frontend uses API data from the backend to provide as complete a picture as possible for traders to make informed decisions when creating an order. For example, a complete or aggregated view of the current orderbook, recent trade history, both generally and for the trader, current open orders from the trader, and potentially additional data such as graphical price history and trends.
Beyond this, the frontend also provides a simple method for traders to create and submit orders, allowing a trader to request an amount of token they wish to buy or sell, and a price point, and whether they want a limit or market order. In order to submit the trade, the user needs to have some sort of wallet set up, and the frontend also provides this capability, both via a built-in wallet available for users to create, and via third party integrations such as MetaMask and Ledger.
The Web interface is designed to also work within dapp browsers, functioning as a dapp.
The API Server is the main source of truth about the current state of the exchange, and provides methods to perform basic exchange actions. This is used both by the web frontend, and is also available for consumption by anyone who wishes to write their own client to interact with the exchange, such as market makers.
The API can be queried for current and historical data about the state of the exchange, such as trade history, orderbook status, etc. It also provides a real time websocket based endpoint that will allow your client to listen for events that change the state of the exchange, such new trades, or settlement of current orders.
In addition, you may submit new data to the exchange through the API Server. It has methods to help a trader construct a valid order for submission to the Matching Engine. A trader construct an order, sign the data received, then immediately submit it back through the API where it will be validated and submitted to the orderbook. A trader may also query their open orders, and choose to cancel orders which will remove the order data and prevent it from being matched.
A relayer chooses what fee rates to charge its traders, and may specify different rates for both makers and takers. Additionally, a relayer may choose to allow certain addresses to receive a rebate on fees as a way to reward high value traders. These rates are built into the order data that is constructed and sent to a trader for signing, and will be validated before submission to the matching engine.
The Websocket Server is responsible for delivering real-time notifications to the client when certain state is changed in the exchange. In order to do so, it must keep an up to date list of clients listening for events, as well as information about which events the client is interested in receiving. It is also responsible for maintaining a number of keep-alive connections with each client that has subscribed for information.
When the Matching Engine has updates about the orderbook, the Websocket Server is responsible for interpreting them, and sending the appropriate messages to subscribers. For example, a client asking for level 3 updates on the orderbook will essentially have the events passed through directly to them, while a client interested in level 2 updates (aggregated orderbook data) will require constructing a message indicating which price levels have been modified due to the orderbook change event.
Events from the blockchain, including updates to user generated transactions (e.g. Approve tokens, Wrap ETH), and updates to the state of trade transactions will also be sent to the appropriate channels. Finally, user specific state such as changes of order status, and locked balances are also communicated through the Websocket.
The Matching Engine is generally responsible for determining if a set of two or more orders "match" and thus should be submitted to the blockchain for verification and settlement. In order to do this it is also responsible for a number of other tasks related to orders.
First, the engine is responsible for maintaining a valid orderbook for each market (base/quote token pair) in which it accepts orders. It does this by receiving new order data, cancelled order data, and transaction success/failure events from the exchange contract, and using that data to mutate the orderbook as necessary. Whenever an update is made to the orderbook, the engine will send an orderbook change event to the Websocket Server, which will then be pushed to any interested clients.
Next, with a valid orderbook, the engine is responsible for taking incoming order data and determining if it is necessary to submit a match to the exchange contract. It does this by looking for any current orders which are capable of filling the incoming order in a valid way, either fully or partially. If an incoming order is capable of being matched by multiple orders, the engine will prioritize the matching orders first by price, then by time submitted. This means that a matching order offering a better price to a trader will always be prioritized, no matter when it was submitted to the orderbook. If two matching orders have an identical price, the one that was submitted to the orderbook first will always be prioritized over the one submitted at a later time.
Then, once all valid matching orders have been discovered and prioritized, the engine will generate data for a transaction to be submitted to the exchange contract, and pass it to the Transaction Launcher. It has then completed its duties until an event comes back from the blockchain. Once the transaction is successful, the orderbook will be updated as appropriate.
If there is an error with the transaction, it will be communicated the engine and appropriate action will be taken. If it is a transient error, the engine may retry the transaction. If an order has become invalid, it may be necessary to make changes to the orderbook and attempt to generate a new matching trade.
The final duty of the Matching Engine is to keep track of locked balances for each account address in the orderbook - essentially a tally of how much token is currently tied up for each trader. The relayer should block a trader from submitting orders that would increase their total locked balanced beyond the amount of token they actually have in their wallet.
The Transaction Launcher is responsible for signing, submitting, and managing the transactions that are sent to the exchange contract. When it receives transaction data from the matching engine, it signs that data with the relayer's private key before submitting it to the exchange contract to be validated and settled. It is also responsible for monitoring a transactions state, particularly for long pending transactions. If a transaction stays in the pending state for too long, the Transaction Launcher may choose to cancel and resubmit the transaction with a higher gas price.
The Blockchain Watcher is responsible for managing events emitted from the blockchain and routing them to the proper component. When the exchange contract submits a success or failure event, they will be routed to the Matching Engine to be used to update the orderbook. Additionally the watcher will be paying attention to numerous other transactions in order to provide updates for user specific transactions, such as token approvals and wrapping ETH.
The watcher tries to strike a balance between speed and accuracy. To do so, it maintains two pointers to the blockchain. Every new block is scanned for relevant messages immediately, but it also maintains a pointer to scan 10 blocks backwards in history. The first pointer will help to update status as quickly as possible, but may not be 100% accurate. The second pointer will let it determine if a block becomes orphaned, and allow repairs to be made to the relayer state.