# Sifchain DEX Liquidity Protection
"Keeping your DEX's liquidity in good hands!"
## 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.

### Variable Definitions
#### Chain State
The chain state is the set of information from the consensus protocol. This is exogenous to the model of the liquidity pool in that blocks are validated without feedback loop that prevents the operation of the protocol. The variables specified below are the state of the chain necessary to be read by the LPD monetary policy.
| Variable Name | Descriptive Name | Description | Symbol |
|----|----|----|----|
| start_block_height | Start Block Height| First block of a given policy period| $h_S$ |
| final_block_height| Final Block Height | Last block of a given policy period| $h_F$ |
| current_block | Current Block | Current block | $h$ |
#### Governance Decision
The governance decision is a policy input message that broadcasts a specified $r_{gov}$ for a specified length of time $l_{policy}$, with a computable $r_{final}$ as the intentional end-state of the policy at the end of its effective life at $h_f$.
| Variable Name | Descriptive Name | Description | Symbol | Domain|
|----|----|----|----|----|
| epoch_length | Epoch Length | Number of blocks in an epoch | $l_{epoch}$ | $\mathcal{I} \geq 0$ |
| max_rowan_liquidity_threshold_percentage | Max Rowan Liquidity Threshold Percentage| Percentage of Rowan liquidity allowed to move out of a pool in a given epoch | $r_{MAX-LIQ}$ | $0 \leq r_{MAX-LIQ} \leq 1$ |
**Assumption: epoch_length is a governance design, where there is the need for the consensus protocol to execute an epoch module.**
#### Liquidity Pool Module State
Receives LPD policy through governance decision, updating its parameters for $r_{gov}$ and $l_{policy}$. Combining this policy with the production of blocks from the protocol, allows for the computation of $r_{block}$ and $r_{running}$.
| Variable Name | Descriptive Name | Description | Symbol |
|----|----|----|----|
| ALLOW_EXTERNAL_LIQUIDITY_OUT | | Boolean, true if halting action is not met, false if under restriction | $trade_{enabled}$ |
| max_rowan_liquidity_threshold | Max Rowan Liquidity Threshold | Maximum amount of Rowan permitted to move out of a pool at that block, subject to the limit within the epoch | $\tau_{max}$ | $\tau_{max} \geq 0$ |
| current_rowan_liquidity_threshold | | Current allowable Rowan threshold | $\tau_t$ |
## 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.
At the beginning of each epoch, the daily threshold is set to the maximum. The daily threshold is updated per block and its difference equation is conditioned upon the swapping actions in each direction. :
$$\tau_t = \tau_{max} - \sum_{t=0}^{t=t}\Delta x_t \cdot P^Y_t + \sum_{t=0}^{t=t}\frac{\Delta y_t}{P^Y_t}$$
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.