In this series of posts, I will walk you through the Notional V2 smart contracts. The goal of these posts is to help technically minded users and developers better understand a large and complex codebase (~12,000+ lines of Solidity!). If you've made it this far, I'm impressed. You must have some tolerance for pain : ). This post, however, is the most critical one of all. Because the Valuation Framework allows borrowers to hold many forms of collateral against their debts, liquidation must be equally robust. Notional V2's liquidation contracts must be able to handle all potential scenarios, however unlikely they may seem. If it is possible for an account to reach an un-liquidateable position that would be a recipe for disaster.
During liquidation, a liquidator purchases some assets from an account in return for what is termed "local currency". Local currency can be thought of as the currency that debt is denominated in. By reducing the net local currency debt, the account's free collateral position will increase. For example, if an account has a -100 fUSDC debt and USDC has a 140% collateral requirement, then 140 USDC worth of collateral will be required (i.e. -100 * 140% = 140). However, if the account deposits 50 USDC worth of "asset cash" then this will be added before the 140% collateral requirement is applied (i.e. (-100 + 50) * 140% = 70). By adding 50 USDC worth of "asset cash" the account's collateral requirement was reduced by 70 USDC. This 20 USDC difference between the reduction in the account’s collateral requirement and the amount of USDC deposited is what we call the "local currency benefit" and it goes towards increasing an account's free collateral position.
Liquidations must adhere to a number of invariants to ensure that they work properly:
- It's not guaranteed that a liquidation will put an account into positive free collateral, but the free collateral position of the account must always increase as a result of liquidation.
- Liquidations can never increase or incur a debt position.
- Liquidations can never liquidate more than the balance of an asset.
There are two deployed contracts that handle liquidations: LiquidateCurrencyAction.sol and LiquidatefCashAction.sol. There are four types of liquidations: local currency, collateral currency, local currency fCash, and cross currency fcash. Each liquidation type also has an
external "calculate" method that does not update storage. This calculate method can be used to determine the net effects of a liquidation. Because cTokens may need to be compounded to get the most accurate information these cannot be
view methods. These can be called via a static call from off chain.
Pre Liquidation Factors
The first step in liquidation is getLiquidationFactors which ensures that an account has no matured assets and checks the free collateral position of the account to ensure that it is negative. If so, then the liquidation can proceed. There are a number of fields in LiquidationFactors, but most are pretty straightforward. They are saved during free collateral so they don't have to be fetched again later as a gas optimization. There are a few important factors that are worth further discussion.
netETHValueis the aggregate free collateral position of the account denominated in ETH (not cETH). This is used to determine the required liquidation amounts.
localAssetAvailableis the net value of the local currency in asset cash terms before liquidation. We will use this to determine how much "local currency benefit" will be gained during liquidation.
collateralAssetAvailableis the net value of the collateral currency in asset cash terms before liquidation. This figure represents the maximum amount of collateral value that can be sold. If this figure is negative at the end of liquidation then the liquidated account will have incurred a new debt, which is not acceptable. Note that some liquidation types do not require a collateral currency specified.
nTokenHaircutAssetValuewill be used when we determine the balance of nTokens to liquidate. Since nTokens cost a fair bit of gas to get the present value for, we save off the value during the free collateral calculation.
nTokenParameterswill also be saved off for nToken calculations.
Notional V2 allows liquidators to purchase up to 40% of an account's collateral value (i.e. DEFAULT_LIQUIDATION_PORTION). This ensures that liquidation can return an account to a position where it is more than minimally collateralized.. It also gives the liquidator more profitability to cover potential high gas or trading costs. If the account requires more collateral to be liquidated to reach minimal collateralization then the liquidation will go past 40%. Liquidators can also specify a maximum amount of a particular asset that they want to liquidate. This logic can be seen in calculateLiquidationAmount.
Liquidating Currency Balances
Currency liquidations are implemented in LiquidateCurrency.sol. Currency liquidations target asset cash balances, nToken balances, and liquidity tokens as potential sources of collateral.
Local currency liquidation is likely to be a rare occurrence. An account can enter a position where they are borrowing against nTokens or liquidity tokens denominated in the same currency and the value of those tokens decreases. The value of these tokens will change with interest rates.
Without rehashing the entire
liquidateLocalCurrency method, I'll point out a few things:
- The amount of "recollateralization" from
liquidateLocalCurrencyis defined by the difference between the actual value of liquidity tokens and nTokens and the haircut value minus incentives paid to the liquidator. You can think of this method as converting relatively risky nToken and liquidity token assets back into risk-free cash balances.
- Most of the complexity is in
_withdrawLocalLiquidityTokens. This method loops through a portfolio and removes liquidity tokens from fCash markets in order to withdraw enough cash to satisfy
assetAmountRemaining. At each withdrawal an incentive is paid to the liquidator.
- Note that when withdrawing liquidity tokens the fCash claim will be credited back into the account which will also add to its free collateral position. This is not considered at all in the method but will have the effect of increasing the free collateral position by the liquidity token haircut amount. Also, this creates an edge case where an array type portfolio may end up with more than the portfolio assets the normal limit.
- The returned
netAssetCashFromLiquidatormay be positive or negative for this method. If the liquidator is purchasing nTokens then it will be positive. It will be negative if the liquidator is receiving the liquidity token repo incentive. It may be either sign if both events occur.
Collateral currency liquidation will be the most common liquidation type. Borrowers will collateralize their stablecoin debts with tokens like ETH (or nETH) and the price of those tokens may fall.
liquidateCollateralCurrency handles these scenarios. Again, without rehashing the entire method I'll point out a few things:
- The liquidation goes in the order of asset cash balances, then withdrawing collateral currency liquidity tokens, then nTokens.
- Withdrawing collateral currency liquidity tokens is a different method for collateral currency liquidations. It's largely the same but simpler because no incentive is paid to the liquidator. Again, only cash claims are considered (not the fCash residual).
calculateLocalToPurchaselimits the amount of collateral purchased to ensure that
localAssetAvailabledoes not become positive.
fCash liquidation is required because Notional V2 allows accounts to hold positive fCash loans as collateral against negative fCash debts. This is a powerful feature that enables sophisticated trading and market making to occur in Notional V2.
A user could borrow from one fCash market and use the proceeds to lend to another fCash market while holding a much smaller amount of collateral relative to the size of their borrowing. Users might want to do this to take advantage of interest rate differentials between different fCash markets. Because fCash can be used as collateral against a debt, users can execute these trades with leverage. Since fCash has different dynamics than asset cash balances, liquidating fCash positions requires a different approach.
Local Currency fCash
This liquidation will arise when an account has positive and negative fCash in a single currency (i.e. the local currency). For example, an account may be speculating on the future course of interest rates for a given fCash market. For example, they may have borrowed from the three month fCash market and lent to the one year fCash market.
- If interest rates in the three month fCash market (where the account has borrowed) increase, they will have a profit (i.e. they can lend at a higher rate than they borrowed).
- If interest rates in the three month fCash market decrease, they will have a loss (i.e. lending will cost more than they borrowed).
- The inverse is true in the one year fCash market. An increase in rates means that borrowing to offset their loan will cost more and their profit will decrease. A decrease in rates means it will be cheaper and their profit will increase.
If the spread between the three month
oracleRate and the one year
oracleRate widens (i.e. the one year rate increases and/or the three month rate decreases), this user's account may become undercollateralized. In this case, a liquidator will be able to liquidatefCashLocal. Again, without rehashing the entire method here are a few things to notice:
- Liquidators can specify which maturities of fCash they wish to purchase. These fCash assets may have either positive or negative notional values.
- Generally liquidators will purchase positive fCash, but there is an edge case where an account holds negative fCash and a cash balance in the same currency. If interest rates decrease (i.e. lending costs more) the account may become undercollateralized. In this case, the liquidator would take the account’s negative fCash and cash balance. In effect, the liquidator will be borrowing at a below-market rate. The liquidator could then lend out the capital that he borrowed on the relevant fCash market at the fair market rate to complete the arbitrage. Again, it's highly unlikely that this would occur but we need to account for it during liquidation.
- When reading the code, note that fCash is denominated in underlying and asset cash balances (which the liquidator will deposit) are in a different denomination.
- Note that liquidating fCash requires discounting fCash amounts to present value using the exponent function. In both local fCash and cross currency fCash this means that the
underlyingBenefitRequiredmay never exactly equal zero. Both methods stop liquidation when the potential benefit is below
Cross Currency fCash
The dynamics of cross currency fCash are similar to local currency fCash liquidations. The main difference is that the fCash collateral will be denominated in a different currency than the local currency (where the debt is). An account may be eligible for cross currency fCash liquidation if they have fETH collateral against an fDAI debt. In this case it's most likely that the exchange rate of ETH to DAI has fallen. The free collateral gained when the liquidator purchases the fETH in exchange for DAI asset cash is called the
An account may also be eligible for cross currency fCash liquidation if it is borrowing fDAI and lending in fUSDC. Assuming that the stablecoins are still holding their pegs, the source of undercollateralization will be the divergence of Notional fCash market interest rates between the two currencies. The free collateral gained when the liquidator purchases fUSDC in exchange for DAI asset cash is called the
fCashBenefit are calculated in this method.
Liquidations in Notional V2 can occur in a lot of scenarios. However, we believe that the vast majority of liquidations will be collateral currency liquidations and look very similar to liquidations in other lending protocols. It's never good, to leave things to chance though. Designing and auditing Notional V2's liquidation system requires a thorough analysis of how risk may arise given the sophisticated valuation framework and understanding how a liquidator may profitably unwind that risk.
Ultimately, Notional V2's goal is to be the most capital efficient lending platform in DeFi and that requires a sophisticated liquidation system to match.
Notional Finance Newsletter
Join the newsletter to receive the latest updates in your inbox.