# Acala & Karura CDP Liquidation ## Trigger liquidations by off-chain worker On every block, an off-chain worker will run to check if there is any unsafe CDP that needs to be liquidated. The [off-chain worker](https://docs.substrate.io/fundamentals/offchain-operations/) is an integral part of the blockchain, it replaces the Keeper system as a more secure and efficient way to monitor and trigger blockchain state changes. On each off-chain worker running, it will check a maximum 1000 number of CDPs. If the maximum is reached, the next run will pick up what was left and check. - The first time when the check runs, a random one will be picked from the collateral type list, and off-chain work will check its CDPs. - If all the CDPs of a certain collateral type are checked, but the maximum check amount is not reached yet, the check will continue with the next collateral type on the collateral list. In addition, the CDP location cursor for the next check of the current type will be reset. For each CDP, if it's unsafe and there was no emergency shutdown, it will be liquidated Or if there is an emergency shutdown and this CDP has any debit, it will be settled. ## Liquidation process ```rust fn liquidate_unsafe_cdp(owner, currency_id) ``` Liquidation is an automatic process of transferring collaterals and debts of an unsafe CDP to the protocol. Then the protocol will aim to sell the collateral for enough aUSD to repay outstanding debts. The liquidation process uses a hybrid mechanism of liquidating via DEX, smart contracts, and auctions to optimize capital efficiency for vault owners. If the position of `currency_id` to liquidate is safe, the `MustBeUnsafe` error will be returned. The target aUSD amount for repayment is the sum of aUSD owed plus the liquidation penalty, calculated by: `target = exchange_rate * debit_amount * (1 + liquidity_penalty)`, where: - `exchange_rate` is read from the module storage `DebitExchangeRate`, or if it hasn't been set yet, the default value `DefaultDebitExchangeRate` will be used. - `debit_amount` is the debit amount in position. - The risk parameter `liquidity_penalty` is read from the CDP engine module storage `CollateralParams`, or if it hasn't been set yet, the default value `DefaultLiquidationPenalty` will be used. The first step of liquidation is to transfer all collateral and debit in this CDP to the CDP treasury. If `currency_id` is an LP share, the share will be withdrawn from the CDP treasury module account first. Depending on the token types in it: - If one of the tokens in the trading pair is aUSD, as the amount `x`: - If `x` is greater than target, the amount `x - target` will be refunded to `owner`, i.e. transferred from the CDP treasury module to them. - The additional aUSD required for repayment will be `target - x`. - If none of the assets in the trading pair is aUSD, the `target` aUSD amount will be split into two halves, where each half is the target amount for each token in the trading pair. The liquidation of collateral currency/asset is handled in prioritized approaches, the priority is immediate liquidation > collateral auctions: - Immediate liquidation is executed with on-chain participants, including the Acala/Karura DEX and smart contracts. - If immediate liquidation failed, collateral auctions would be initiated. Emits the `LiquidateUnsafeCDP` event if liquidation is successful, which includes: - `collateral_type`: the collateral type - `owner`: the owner of the CDP - `collateral_amount`: the liquidated collateral amount - `bad_debt_value`: the bad debt - `target_amount` : the target aUSD amount for repayment ### Immediate Liquidation ```rust fn liquidate(owner, currency_id, amount, target) ``` 1. Liquidation via DEX is tried first. - If `collateral_liquidated_price / collateral_oracle_price` is at least `ImmediateLiquidationPriceRatio` (the CDP engine module config), the liquidation is done, or the state is rolled back. - Else if below this ratio, liquidation via Contracts will be tried as follows. 2. Liquidation via contracts is tried. - If it's successful, the liquidation is done. - Else, the best price ratio from contract liquidation is compared with the price ratio from DEX as follows. 3. DEX and contracts price ratio comparison: - If the DEX price ratio is better and it's greater than `MinLiquidationPriceRatio` (the CDP engine module config), the liquidation is executed by the DEX. - Or if the contract price ratio is better and it's greater than `MinLiquidationPriceRatio`, the liquidation is executed by the smart contract. 4. If the above approaches all fail, the immediate liquidation fails and the `LiquidationFailed` error is returned. - Collateral auctions would be created then. #### Liquidation via DEX ```rust fn liquidate_via_dex(owner, currency_id, amount, target) ``` - The CDP treasury module is called to swap `amount` of collateral to at least `target` amount of aUSD. The actual collateral supply and target amount is returned from the call. - The refundable collateral amount is `amount - acutal_supply`. If it's not zero, it is transferred from the CDP treasury module account to owner. - The refundable aUSD amount is `actual_target - target`. If it's not zero, it is transferred from the CDP treasury module account to `owner`. #### Liquidation via contracts ```rust fn try_immediate_liquidation_via_contracts(currency_id, amount, target, collateral_oracle_price) ``` ##### Contract interface - `liquidate(address collateral, address repay_dest, uint256 amount, uint256 min_repayment)` - `onCollateralTransfer(address collateral, uint256 amount)` Note for all calls from Karura/Acala runtime, the `sender` will be `0x31382d495fed5a6820d9c07e8b6efe8d2166e9dd`, which liquidation contracts should check for security reason. Gas fee will be charged from contract address, which should be guaranteed to be sufficient. ##### Contract liquidation process - The module storage `LiquidationContracts` stores a list of registered liquidation contract addresses. Each of them is tried to liquidate in the following order: - The contract at the register list index `current_block_number % list_length` is selected as the first. If it fails, the next contract is tried, and vice versa. - When the last in the liquidation contracts list fails, the first contract in the list will be tried, until the contract at the index `current_block_number % list_length` is reached again. - A contract liquidates with the following steps: - The the contract `liquidate` function is called via the CDP engine module config `LiquidationEvmBridge`. If the call is successful, the contract is expected to transfer the target amount of aUSD to the CDP engine module account. - If enough aUSD payment is received from the contract, `amount` of collateral is transferred from the CDP treasury module account to the contract address, and the contract will be notified the collateral transfer is finished via the function call `onCollateralTransfer`. - The liquidation by this contract is successful if `collateral_liquidated_price / collateral_oracle_price` is at least `ImmediateLiquidationPriceRatio`, or the state is rolled back and the price ratio is recorded. - If all the contracts failed to liquidate with `ImmediateLiquidationPriceRatio` requirements, their price ratios are compared and the highest is returned along with the contract address. #### Liquidation Examples Current state: - `ImmediateLiquidationPriceRatio` is 0.9. - `MinLiquidationPriceRatio` is 0.85. - DOT oracle price: 5.5 ##### Example 1 Alice's DOT position is to be liquidated: 100 DOT for 460 aUSD. Try to liquidate via DEX: - Swapped 100 DOT to 535 aUSD. - The price ratio is `5.35 / 5.5 = 0.9728`, which is greater than 0.9. - 75 aUSD is refunded to Alice. - Liquidation is successful. Result: immediate liquidation is successful via DEX. ##### Example 2 Alice's DOT position is to be liquidated: 1000 DOT for 4600 aUSD. Try to liquidate via DEX: - Swapped 1000 DOT to 4900 aUSD. - The price ratio is `4.9 / 5.5 = 0.891`, which is smaller than 0.9. - The state is rolled back. Try to liquidate via contracts: - Try the first contract: - The contract can liquidate 1000 DOT for 4910 aUSD. - The price ratio is `4.91 / 5.5 = 0.8928`, which is smaller than 0.9. - The state is rolled back. - Try the second contract: - The contract can liquidate 1000 DOT for 5200 aUSD. - The price ratio is `5.2 / 5.5 = 0.945`, which is greater than 0.9. - Liquidation is successful. Result: immediate liquidation is successful via a smart contract. #### Example 3 Alice's DOT position is to be liquidated: 2000 DOT for 9000 aUSD. Try to liquidate via DEX: - Swapped 2000 DOT to 9600 aUSD. - The price ratio is `4.8 / 5.5 = 0.8727`, which is smaller than 0.9. - The state is rolled back. Try to liquidate via contracts: - No contracts can liquidate with above the 0.9 price ratio, the best is 0.86. - The state is rolled back. The price ratio of DEX 0.87 is higher and it's greater than 0.85. - Swap 2000 DOT for 9600 aUSD. - Refund 600 aUSD to Alice. - Liquidation is successful. Result: immediate liquidation is successful via DEX. #### Example 4 Alice's DOT position is to be liquidated: 5000 DOT for 23000 aUSD. Try to liquidate via DEX: - Swapped 5000 DOT to 23100. - The price ratio is `4.62 / 5.5 = 0.84`, which is smaller than 0.9. - The state is rolled back. Try to liquidate via contracts: - No contracts can liquidate with above the 0.9 price ratio, the best is 0.845. - The state is rolled back. Both DEX and contract price ratios are below 0.85. Result: immediate liquidation failed, and 5000 DOT will be auctioned. ### Liquidation via auction ```rust fn liquidate(owner, currency_id, collateral_amount, target) ``` If all smart contracts failed to liquidate, the CDP treasury module will create auction(s) to liquidate the collateral.