Published on
【Free MEV系列】| Compound V2 清算
Authors
  • avatar
    Name
    thinkingchaindotapp
    Twitter

Compound V2 清算

原理

Compound V2的清算比AAVE简洁很多,它提供了非常方便的接口供我们清算。

它提供了这个函数:

function getAccountLiquidity(address account) view returns (uint, uint, uint)

account: The account whose liquidity shall be calculated.

RETURN: Tuple of values (error, liquidity, shortfall). The error shall be 0 on success, otherwise an error code. A non-zero liquidity value indicates the account has available account liquidity. A non-zero shortfall value indicates the account is currently below his/her collateral requirement and is subject to liquidation. At most one of liquidity or shortfall shall be non-zero.

  • 输入:需要清算的账户
  • 输出
    • 清算成功返回0,否则是错误代码
    • 非零的流动性值表示账户有可用的账户流动性
    • 非零的缺口值表示账户当前低于其抵押要求,并可能面临清算。流动性和缺口值最多只有一个为非零。

然后我们只需要监听这个接口的返回值,进行清算就好了。下面是我们清算之后的输出:

  [Before] liquidity: 0.000000000000000000
  [Before] shortfall: 26.392206940915889741
  [Before] my cETH: 0.00000000
  [After] liquidity: 332.950243140081219857
  [After] shortfall: 0.000000000000000000
  [After] my cETH: 88730.60279429

我们将缺口shortfall完全清算了。使用218213个USDC获得了88730个ETH

代码

//SPDX-License-Identifier: Unlicense
pragma solidity =0.8.19;

import "forge-std/Test.sol";
import "../interface.sol";

// @tx hash: 0xa93bc349561d1f3d834b3c645864a3cb618be747ef4ec66d71c6a5512eeafff6
// @Profit : 10910.69784 $
// Collateralized Token: ETH
// Repaid Token: USDC

contract LiquidationOperator is Test {
    ICompound_v2_cToken public cETH = ICompound_v2_cToken(0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5);
    ICompound_v2_cToken public cUSDC = ICompound_v2_cToken(0x39AA39c021dfbaE8faC545936693aC917d5E7563);
    ICompound_v2_comptroller public comptroller = ICompound_v2_comptroller(0x3d9819210A31b4961b30EF54bE2aeD79B9c9Cd3B);
    address public user = 0x39bDe2F9254cfEf7d0487a27E107Ef6C1685e44c;

    IERC20 public usdc = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);

    function setUp() public {
        // hash in eth: 0xa93bc349561d1f3d834b3c645864a3cb618be747ef4ec66d71c6a5512eeafff6
        vm.createSelectFork("mainnet", 9120962 - 1);
        vm.label(address(cETH), "cETH");
        vm.label(address(cUSDC), "cUSDC");
        vm.label(address(comptroller), "comptroller");
        vm.label(address(user), "user");
        vm.label(address(usdc), "usdc");

        deal(address(usdc), address(this), type(uint256).max);
    }

    function test_liquidation() public {
        // https://docs.compound.finance/v2/comptroller/#account-liquidity
        // A non-zero liquidity value indicates the account has available account liquidity. 
        // A non-zero shortfall value indicates the account is currently below his/her collateral requirement and is subject to liquidation. 
        (, uint256 liquidity1, uint256 shortfall1) = comptroller.getAccountLiquidity(user);
        require(shortfall1 > 0 && liquidity1 == 0, "Can not be liquidated");

        emit log_named_decimal_uint("[Before] liquidity", liquidity1, 18);
        emit log_named_decimal_uint("[Before] shortfall", shortfall1, 18);
        emit log_named_decimal_uint("[Before] my cETH", cETH.balanceOf(address(this)), 8);

        usdc.approve(address(cUSDC), 218213956720);
        cUSDC.liquidateBorrow(user, 218213956720, address(cETH));

        (, uint256 liquidity2, uint256 shortfall2) = comptroller.getAccountLiquidity(user);
        emit log_named_decimal_uint("[After] liquidity", liquidity2, 18);
        emit log_named_decimal_uint("[After] shortfall", shortfall2, 18);
        emit log_named_decimal_uint("[After] my cETH", cETH.balanceOf(address(this)), 8);

    }    

}

Tags