owned this note
owned this note
Published
Linked with GitHub
# Sifchain DEX Liquidity Protection
"Keeping your DEX's liquidity in good hands!"
[Functional Doc](https://www.notion.so/sifchain/DEX-Liquidity-Protection-Functional-Doc-External-4525330ebfc34e029cb6e5f967704c77)
## Motivation
Rational actors may choose to buy and hold Rowan indefinitely to access Sifchain's world class cryptoeconomic value generators, but they will also want to occasionally take profit to pay operational costs and rebalance to mitigate risk. Such rational actors will be open to coordinating any profit taking or rebalancing with their fellow Rowan holders so to maintain the usability of the DEX's liquidity for all Rowan holders.
Liquidity protection is the mechanism by which they coordinate. It is an on-chain maximum amount of non-Rowan liquidity (aka "external liquidity") on Sifchain that can be bought at any given time duration.
![](https://hackmd.io/_uploads/SyXM5QkTc.png)
## MVP
In the base case, SifDAO can simply allow or disallow external liquidity from leaving at all based on its view of market activity. Let $t$ be a binary toggle that is either TRUE or FALSE. Every swap of Rowan across all pools is subject to the following logic.
```
init() {
ALLOW_EXTERNAL_LIQUIDITY_OUT? = t
}
rowan_swap_threshold_checker(swap_type) {
if (swap_type == SELL_ROWAN and ALLOW_EXTERNAL_LIQUIDITY_OUT? == FALSE ){
reject_sale() // The swapper retains the Rowan they attempted to sell
} else {
approve_sale() // The swap proceeds as normal
}
}
```
These properties satisfy the goal for our original motivation. However, they put a lot of burden on SifDAO for manual governance. We can improve this with some simple logic.
## Flat Daily Threshold
With a flat threshold, SifDAO will be able to set the liquidity protection threshold in USD corresponding to the net amount of Rowan they'd like to be sellable in any given epoch (assumed to be roughly one day in block time).
Let $l$ be the liquidity protection threshold set by governance.
Every swap of Rowan across all pools is subject to the following logic.
```
init() {
max_rowan_liquidity_threshold = l
current_rowan_liquidity_threshold = max_rowan_liquidity_threshold
epoch_length = // number of blocks per epoch, presumably set to approximate day per epoch
}
lookup_rowan_oracle_price() {
// The current block's ratio price for Rowan:USDT cannot be used due to the risk of an oracle attack.
// We could use a block-based TWAP or some other solution, see here for further research
// https://blog.euler.finance/uniswap-oracle-attack-simulator-42d18adf65af
// https://smartcontentpublication.medium.com/twap-oracles-vs-chainlink-price-feeds-a-comparative-analysis-8155a3483cbd
}
rowan_swap_threshold_checker(rowan_swap_amount, swap_type) {
rowan_price = lookup_rowan_oracle_price()
if (swap_type == SELL_ROWAN){
if (current_rowan_liquidity_threshold - rowan_swap_amount * rowan_price < 0){
reject_sale() // The swapper retains the Rowan they attempted to sell
} else {
current_rowan_liquidity_threshold -= rowan_swap_amount * rowan_price
approve_sale() // The swap proceeds as normal
}
if (swap_type == BUY_ROWAN) {
current_rowan_liquidity_threshold = min(current_rowan_liquidity_threshold + rowan_swap_amount * rowan_price, max_rowan_liquidity_threshold)
}
}
begin_block() {
...
current_rowan_liquidity_threshold = min (current_rowan_liquidity_threshold + max_rowan_liquidity_threshold / epoch_length, max_rowan_liquidity_threshold)
...
}
```
With this logic, we expect the following properties to hold:
* Users can collectively sell Rowan so long as net sales do not exceed `max_rowan_liquidity_threshold` in any given epoch
* Users can collectively sell more than `max_rowan_liquidity_threshold` in an epoch so long as Rowan purchases during that epoch ensure that the net sales of Rowan to not exceed `max_rowan_liquidity_threshold`
* Users who commit double swaps (purchasing Rowan in one pool and then immediately selling it in another pool) will not be affected by this threshold so long as the second part of their double swap is not larger than `max_rowan_liquidity_threshold` by itself
* `current_rowan_liquidity_threshold` for any given user on any given swap will be `max_rowan_liquidity_threshold` if one epoch has elapsed and no net Rowan sales have occurred.
* Replenishment of `current_rowan_liquidity_threshold` will occur evenly on a per-block basis, encouraging an equal distribution of net Rowan sales
These properties reduce SifDAO's need to manually intervene. However, we can improve in this design with a percentage-based threshold.
## Percentage-Based Daily Threshold
The flat threshold requires governance to adjust `max_rowan_liquidity_threshold` from time to time depending on other market conditions, which may become tedious. Consider this more advanced version in which governance specifies the percentage of non-Rowan liquidity that can be purchased at any point.
Let $m$ be the liquidity protection threshold percentage set by governance.
Every swap of Rowan across all pools is subject to the following logic.
```
init() {
max_rowan_liquidity_threshold_percentage = m
max_rowan_liquidity_threshold = get_total_external_liquidity() * max_rowan_liquidity_threshold_percentage
current_rowan_liquidity_threshold = max_rowan_liquidity_threshold
epoch_length = // number of blocks per epoch, presumably set to approximate day per epoch
}
lookup_rowan_oracle_price() {
// The current block's ratio price for Rowan:USDT cannot be used due to the risk of an oracle attack.
// We could use a block-based TWAP or some other solution, see here for further research
// https://blog.euler.finance/uniswap-oracle-attack-simulator-42d18adf65af
// https://smartcontentpublication.medium.com/twap-oracles-vs-chainlink-price-feeds-a-comparative-analysis-8155a3483cbd
}
get_total_external_liquidity() {
// returns the USD value of all non-Rowan liquidity on Sifchain
// We can likely estimate this value if a precise value cannot easily be calculated frequently
}
rowan_swap_threshold_checker(rowan_swap_amount, swap_type) {
rowan_price = lookup_rowan_oracle_price()
max_rowan_liquidity_threshold = get_total_external_liquidity() * max_rowan_liquidity_threshold_percentage
if (swap_type == SELL_ROWAN){
if (current_rowan_liquidity_threshold - rowan_swap_amount * rowan_price < 0){
reject_sale() // The swapper retains the Rowan they attempted to sell
} else {
current_rowan_liquidity_threshold -= rowan_swap_amount * rowan_price
approve_sale() // The swap proceeds as normal
}
if (swap_type == BUY_ROWAN) {
current_rowan_liquidity_threshold = min(current_rowan_liquidity_threshold + rowan_swap_amount * rowan_price, max_rowan_liquidity_threshold)
}
}
begin_block() {
...
max_rowan_liquidity_threshold = get_total_external_liquidity() * max_rowan_liquidity_threshold_percentage
current_rowan_liquidity_threshold = min (current_rowan_liquidity_threshold + max_rowan_liquidity_threshold / epoch_length, max_rowan_liquidity_threshold)
...
}
```
With this logic, we expect the properties from the flash threshold to hold, as well as these additional properties:
* As LPs withdraw external liquidity from Sifchain, `max_rowan_liquidity_threshold` is reduced proportionately just in time to be updated for the next swap
* As swappers sell Rowan and remove external liquidity from Sifchain, `max_rowan_liquidity_threshold` is reduced proportionately just in time to be updated for the next swap
* As LPs add external liquidity from Sifchain, `max_rowan_liquidity_threshold` is increased proportionately just in time to be updated for the next swap
* As swappers buy Rowan and add external liquidity from Sifchain, `max_rowan_liquidity_threshold` is increased proportionately just in time to be updated for the next swap
* When `max_rowan_liquidity_threshold` is decreased below `current_rowan_liquidity_threshold`, `current_rowan_liquidity_threshold` is reset to equal `max_rowan_liquidity_threshold` as soon as the next block (nearly immediately)
* When `max_rowan_liquidity_threshold` is increased, `current_rowan_liquidity_threshold` does not immediately increase but does accelerate its per-block increases
This extra logic allows governance to simply set a percentage of total external liquidity that can be swapped away from Sifchain on a per-epoch basis without needing to track changes to liquidity on the DEX.