Pool stabilization strategy
SU is a strategy for the WSTETH/WETH pair of tokens, which works on top of the corresponding Uniswap V3 pool. It holds two positions in this pool, changing them and rebalancing liquidity between them depending on the price in the pool.
Note for future understanding. Some strategy parameters, which are typically from 0 to 1, need to be multiplied by a special constant
) in order to be saved as integers in the code. Hence, when we mention parameters as is, it's mentioned as is, whereas we mention them multiplied by
DENOMINATOR,we mention them with the letter D at the end. For example,
The strategy operates with three vaults, one ERC20 vault, and two UniV3 vaults. Each of those UniV3 vaults holds a Uniswap V3 position on the WSTETH/WETH pair.
One portion of capital (e.g. 5%) is supposed to be stored inside the ERC20 vault, whereas the remaining part is supposed to be distributed between UniV3 vaults. Below, we describe how this distribution works.
The ratio of the portfolio which is allocated to the ERC20 vault is defined by the
0 <= erc20UniV3CapitalRatio <= 1parameter. The remaining part
1 - erc20UniV3CapitalRatiogoes to the Uniswap V3 positions.
The strategy takes UniV3 pool price estimations from the Mellow Oracle, where typically the average is taken between the spot, UniV3 Oracle (last 30 minutes), and Chainlink Oracle prices (but other safety masks can be stated).
Two UniV3 vaults holding the positions are called lower and upper vaults. The strategy has a parameter set in the constructor,
intervalWidthInTicks,which set the width on both positions in ticks. Positions always have to hold the following:
- The lower vault holds a position with bounds
- The upper vault holds a position with bounds
The initial positions should be set in vaults by the strategist in process of the deployment, holding the mentioned property for some
. Then, the strategy itself would change positions in vaults (as described below), always holding that property.
There are three types of rebalances that might be called by an operator (in fact, that would be an off-chain bot operating the strategy).
Imagine the property for positions is held for some
. We denote the current price in ticks as
Then, there are three scenarios.
- . That means that the price is higher than the upper bound of the position of the lower vault. In that case, we firstly drain all liquidity from the position in the lower vault. As token ratios in positions are different, we can't drain all liquidity from the lower vault to the upper vault, so we push to the upper vault as much as we can, and the remains go to the ERC20 vault. Then, the rebalance function has to be called again. Now, as the position in the lower vault contains zero liquidity, we can close it and open a new position with bounds. Finally, we swap the previous lower and upper vaults, keeping the property with a new. Then, a new rebalance is to be called and we're back to choose a scenario in new circumstances.
- . That means that the price is lower than the lower bound of the position of the upper vault. In that case, we firstly drain all liquidity from the position in the upper vault. As token ratios in positions are different, we can't drain all liquidity from the upper vault to the lower vault, so we push to the lower vault as much as we can, and the remains go to the ERC20 vault. Then, the rebalance function has to be called again. Now, as the position in the upper vault contains zero liquidity, we can close it and open a new position with bounds. Finally, we swap the previous lower and upper vaults, keeping the property with a new. Then, a new rebalance is to be called and we're back to choose a scenario in new circumstances.
- . That means that the price is inside both intervals. In this case, if we denote the amount of UniV3 liquidity in the lower vault positions asand the amount of UniV3 liquidity in the lower vault positions as, the following must be obtained eventually.
(i.e. shares of total strategy Uniswap V3 liquidity in the lower and the upper vault). To obtain those ratios, the strategy transfers tokens from one UniV3 vault to another, depending on which vault lacks liquidity. Let
before the rebalance. The rebalance is done only if
> minUniV3LiquidityRatioDeviation, which is a strategy parameter.
By design, rebalances must not change
. Hence, as token ratios in positions are different, additional tokens from the ERC20 vault might be needed for rebalance to succeed. This is the responsibility of the operator to have enough tokens on the ERC20 vault at the time of this rebalance. If there are not enough tokens, rebalance would be done partially (i.e. ratios would become closer to those which needed).
Generally, as can be seen, the operator might need to call several such rebalances to obtain desired positions and token ratios, depending on market circumstances.
This rebalance is to be done to restore the portion of capital in the ERC20 vault as
erc20UniV3CapitalRatio. For that, the following steps are done.
- The capital of each vault, nominated in WETH, is calculated. For that, token amounts are obtained from
tvl(), the average between
maxTvlis taken for each token, and then the total capital is obtained using the price between tokens from the oracle. Let the ERC20 capital be, the lower vault capital be, the upper vault capital be.
- We wantto be equal to
erc20UniV3CapitalRatio. If they differ less than
minErc20UniV3CapitalRatioDeviation, we don't do a rebalance.
- Hence, we calculate, which is the amount of capital to be taken from Uniswap V3 positions to obtain this ratio (of course,can be negative, which means the capital is actually to be taken from the ERC20 vault to Uniswap V3 positions)
- Certain amounts of tokens to be taken are calculated, based on. The tokens are taken from (or to) Uniswap V3 positions keeping the ratio of Uniswap V3 liquidity between them the same.
- If, tokens are just taken from Uniswap V3 positions to the ERC20 vault.
- If, it's also necessary for corresponding amounts of tokens to be on the ERC20 vault (it obviously can be not true, for example, if all the capital is in one token). For achieving that, the operator might use the next kind of rebalance prior to this rebalance (see below).
There is a parameter
erc20TokenRatio, which is what share of the capital of the ERC20 vault we want to have in WSTETH. All the remaining share of capital is to be in WETH.
If the current ratio and the desired ratio differ less than by
minErc20TokenRatioDeviation, we don't do anything.
In the other case, we have to swap some amount of an excessively presented token to get the second token and align the ratio. We do swaps using Cowswap. At first, a preorder is placed on-chain by the operator. Then, an off-chain bot submits the actual Cowswap order using preorder parameters and calls the
signOrder()function. This function verifies that the actual order matches the preorder's requirements and signs the orders using the Cowswap
The only major risk the strategy holds is a sudden de-peg of STETH and subsequent IL of the positions we hold in the corresponding Uniswap V3 pool.