Design document for the Batch Clearing DEX POC
The aim of the batch clearing dex is to enable users to deposit tokens with the aim of being swapped at a fair price with bounded slippage and almost no impermanent loss. To enable this users will deposit tokens during a deposit window; all deposits during this window will be a 'batch'. Once the deposit window is over the 'batch' will be 'locked'. Deposits to the dex will not specify a price for the swap, they will specify an offset to whichever oracle price is received after the 'batch' is 'locked'; those offsets being 0, +10bps, or -10bps. Once the batch is locked there will be a waiting window before the process starts to await an oracle price. Upon receipt of the oracle price, the batch is terminated and the orders are cleared.
For V1, the deposit window will be 10 mins and then a wait time of 2 minutes before awaiting the oracle price. Only the XTZ/USDT pair will be supported for V1
After the termination of the batch, the users that placed orders can retrieve their funds. Either the user's order did not clear and they retrieve their original funds or the order cleared (totally or partially) and the get an execution of their order.
Deposit
The deposit window is open for a finite time period. During the time period users can deposit their tokens for a BUY or SELL order on the token pair along with an offset to the future received oracle price.
For V1, deposit windows won't run sequentially, in that as soon as a deposit window closes another will open straight away. Once the deposit window closes, users will need to wait for that batch to be cleared before another deposit window opens.
Waiting
Once the deposit window has closed there will be a period of 2 minutes prior to awaiting an oracle price. Once that period has elapsed the first received oracle price will close the batch and clearing will start.
Clearing
The clearing process is the process of matching the orders to ensure that all users can trade at the fairest possible price. Upon deposit there will be six categories of order.
Side
Depending on whether you are buying or selling the pair you will either be on the BUY side or the SELL side. For the pair XTZ/USDT, XTZ is the base and USDT is the quote. So if the XTZ/USDT rate was 1.9 you would get 1 unit of XTZ for 1.9 of USDT if I am buying the pair, i.e. on the BUY side. If I am selling the pair, the inverse would be true. The side of the trade is important to understand which token needs to be deposited in a given swap order.
Tolerance
For any deposit, the user can specify the tolerance to the oracle price that they are willing to trade at. This means that each side can further be segregated into their tolerance levels; $Price_{oracle}-10bps$, $Price_{oracle}$ , $Price_{oracle}+10bps$.
Clearing Level
Given the three tolerance levels, we need to find the level at which we will clear the most orders; the clearing level.
Considering that the amount of deposits for each category is different then we have six categories with a differing amount of tokens deposited for each tolerance.
Deposits | P-10bps | P | P+10bps |
---|---|---|---|
BUY | X of USDT | Y of USDT | Z of USDT |
SELL | R of XTZ | S of XTZ | T of XTZ |
An added complexity is that if I am will to buy at $Price_{oracle}+10bps$ then I will also be implicitly interested in buying at $Price_{oracle}$ and $Price_{oracle}-10bps$ as they are both cheaper levels if I am on the BUY side. The converse is true for the sell side in that if I sell for $Price_{oracle}-10bps$, then I would be willing to sell for the higher prices of $Price_{oracle}$ and $Price_{oracle}+10bps$.
Determining the clearing level
Prices | P-10bps | P | P+10bps |
---|---|---|---|
BUY | P / 1.0001 | P | P * 1.0001 |
SELL | 1.0001 / P | 1/P | 1/ (1.0001 * P) |
P-10bps level
Lets take the P-10bps sell level first. All of the buy levels would be interested in buying at that price, so the clearing volume at that level would be:
$$ CP_{P-10bps} = \min(X + Y + Z, R * \dfrac{ 1.0001 }{P}) $$
P level
Lets take the P sell level first. Only the upper 2 buy levels would be interested in buying at that price, but the lower two SELL levels would be interested in selling so the clearing volume at that level would be:
$$ CP_{P} = \min(Y + Z, (R+S) * \dfrac{1}{P}) $$
P+10bps level
Lets take the P+10bps sell level first. All of the sell levels would be interested in selling at that price, but only the upper BUY level would be interested in buying so the clearing volume at that level would be:
$$ CP_{P+10bps} = \min(Z, (R+S+T) * \dfrac{1}{(P * 1.0001)}) $$
Illustrative Examples
If the Oracle price for XTZ/USDT is 1.9 and the tolerance is +/- 10 basis points, then the six price levels are:
Price Levels | BUY | SELL |
---|---|---|
Price + 10bps | 1.90019 | 0.52626 |
Price | 1.9 | 0.52631 |
Price - 10bps | 1.89981 | 0.52636 |
Assuming these levels we can determine some very basic illustrative examples of different market scenarios:
MARKET | AMOUNTS SKEW | BUY X @ (P-) | BUY Y @ (P) | BUY Z @ (P+) | SELL R @ (P-) | SELL @ S (P) | SELL T @ (P+) | Orders cleared @ P-10bps | Orders cleared @ P | Orders cleared @ P+10bps | Clearance Level |
---|---|---|---|---|---|---|---|---|---|---|---|
SELL PRESSUE | CENTERED | 55 | 100 | 45 | 1000 | 1900 | 900 | 200 | 155 | 55 | P-10bps |
SELL PRESSUE | NEG | 100 | 55 | 45 | 1900 | 1000 | 900 | 200 | 155 | 100 | P-10bps |
SELL PRESSUE | POS | 45 | 55 | 100 | 900 | 1000 | 1900 | 200 | 100 | 45 | P-10bps |
BUY PRESSURE | CENTERED | 250 | 100 | 250 | 95 | 190 | 95 | 50 | 150 | 200 | P+10bps |
BUY PRESSURE | NEG | 250 | 100 | 250 | 190 | 95 | 95 | 100 | 150 | 200 | P+10bps |
BUY PRESSURE | POS | 250 | 100 | 250 | 95 | 95 | 190 | 50 | 100 | 200 | P+10bps |
BALANCED | CENTERED | 50 | 101 | 50 | 95 | 190 | 95 | 50 | 150 | 50 | P |
BALANCED | NEG | 101 | 50 | 50 | 190 | 95 | 95 | 100 | 150 | 101 | P |
BALANCED | POS | 50 | 50 | 101 | 95 | 95 | 190 | 50 | 100 | 50 | P |
BALANCED | OPPOSING (NEG) | 50 | 50 | 101 | 190 | 95 | 95 | 100 | 100 | 50 | P-10bps |
BALANCED | OPPOSING (POS) | 101 | 50 | 50 | 95 | 95 | 190 | 50 | 100 | 101 | P+10bps |
Once we know the clearing level and the volume that can be cleared at that level, we will know how many can be matched (some partially) and those will receive pro-rata execution of their orders. For those that bid outside of the clearing level they will receive their deposits back when they claim.
A Google Sheet with these calculations in is available.
Claiming
After clearing, users can claim their 'results', whether that be their original deposits, a partially matched order result or a fully filled order for the opposing token.
Timelines
gantt
dateFormat YYYY-MM-DD
title Timelines for Batcher going to mainnet
excludes weekends
section Development
End of internal audit :ia, 2023-01-09, 4d
Fee Burning :fb, 2023-01-09, 4d
Oracle :or, 2023-01-09, 10d
Multiple Token Pools (incl. contract management and multisig) :mtp, after fb, 3w
Stabilisation / Final Testing :ft, after mtp, 5d
Mainnet launch :mn, after ft, 3d
section Engagement
Blog post for fee burning :bpfb, 2023-01-20, 5d
Blog post for token pools :bptp, 2023-01-27, 5d
Blog post for oracle :bpor, 2023-02-10, 5d
Blog post for mainnet :bpmn, 2023-02-20, 5d
Community Market Maker
The community market maker is functionality to allow community members to provide liquidity to Batcher in return for a share of the trading fees.
Providing liquidity
Any user can supply liquidity by depositing tokens into the market vault for that token. In return, the depositor will receive a market vault token that represents the user's share of the vault holdings. The market vault token can be redeemed at any time for the share of the holdings that currently exist in the market vault.
Liquidity Injection
Liquidity in the market vault will be used by Batcher in batches where there is no market on one side of the trading pair. If liquidity is used then all trading fees in that batch will be given to the market vault for the token which was used to supply liquidity. These fees will be shared among the market vault participants according to the percentage of market value token a given user has. The amount of liquidity that is supplied to Batcher will be less than or equal to the position on the opposing size of the trade. This all depends on the liquidity available in the vault at a given time.
Auto-redemption
When a batch is cleared that has used the liquidity in a market vault, the order that was placed to supply liquidity will be automatically redeemed. If the order was filled, the tokens redeemed will be stored in the market vault as foreign tokens. For example, if an order supplying liquidity from the tzBTC vault is filled and receives USDT upon redemption then the USDT will be allocated to the tzBTC vault as a foreign token.
Auto-exchange of foreign tokens
If two vaults have a sufficient amount foreign tokens that can be exchanged with each other than Batcher will swap these amounts after receiving a next oracle price for the given pair of tokens. For example, assuming the tzBTC vault holds some USDT as foreign tokens and the USDT vault holds some tzBTC as foreign tokens then these will automatically be swapped between the market vaults using the oracle price.
Redemption
A user can redeem a share of the market vault by depositing an amount of the market vault token which will be burned and the equivalent amount of tokens will be redeemed to the original deposit address. In addition to the redemption of tokens, the share of accumulated fees (in tez) will also will also be returned to the user.
Redemption of foreign tokens
If the market vault holds a number of foreign tokens at the point of redemption then the holdings returned to the user will be the equivalent share of the market vault token along with an equivalent shares of foreign tokens held by the vault.
Relationship between vaults.
There is NO relationship between the market vaults. Each vault is treated independently of each other and as such DOES NOT work like a constant sum or constant product liquidity pool in the traditional sense.
Impermanent Loss
As stated the market vault doesn't work in the same way that a typical liquidity pool does. That said, there are potential loss vectors for a given provider, this will be due to the time that the vault has foreign tokens prior to swapping back to the market token.
- Loss due to missing out on price appreciation of non-stable token. As an example if a provider has deposited tzBTC in to the tzBTC vault and some of that liquidity has been used in the tzBTC/USDT pair then for an amount of time, the market vault will hold a portion of the original tzBTC as foreign tokens in the form of USDT. If during that period, the price of tzBTC were to rise then the provider would miss out on that price appreciation relative to just holding the original amount of tzBTC.
- Loss due to depreciation of the non-stable token. This is the equivalent to holding something like tzBTC and the price depreciates resulting in a loss.
Both types of loss are to some degree offset by the reward of trading fees from the use of liquidity within Batcher.
Gain
Whilst a provider can incur losses by providing liquidity, there is also potential for gain (or at least the insulation against loss). Lets assume that some amoutn of the tzBTC vault has been used as liquidity in Batcher. The tzBTC vault will hold an amount of USDT as foreign tokens. If, prior to those tokense bing swapped back to tzBTC, the price of tzBTC depreciates, then at the point of swap the vault will aquire more tzBTC than was previously swapped resulting in a net gain in the quantity of tzBTC.