In this section, we simulate a borrow->liquidation scenario:
Step 1: supply
Step 2: borrow max
Step 3: wait few blocks and let borrowed_balance > supplied_balance * collateral_factor -> leads to liquidation
Step 4: liquidate
Code:
Setup
Set Comptroller, borrowed token and borrowed cToken:
Comptroller public comptroller = Comptroller(0x3d9819210A31b4961b30EF54bE2aeD79B9c9Cd3B);
IERC20 public tokenBorrow;
CErc20 public cTokenBorrow;
event Log(string message, uint val);
constructor(address _tokenBorrow, address _cTokenBorrow) {
tokenBorrow = IERC20(_tokenBorrow);
cTokenBorrow = CErc20(_cTokenBorrow);
}
Close Factor
What is "close factor"? Quote from doc:
The percent, ranging from 0% to 100%, of a liquidatable account’s borrow that can be repaid in a single liquidate transaction. If a user has multiple borrowed assets, the closeFactor applies to any single borrowed asset, not the aggregated value of a user’s outstanding borrowing.
Close factor can be queried via comptroller.closeFactorMantissa():
// close factor
function getCloseFactor() external view returns (uint) {
return comptroller.closeFactorMantissa();
}
Liquidation Incentive
What is "liquidation incentive"? Quote from doc:
The additional collateral given to liquidators as an incentive to perform liquidation of underwater accounts. A portion of this is given to the collateral cToken reserves as determined by the seize share. The seize share is assumed to be 0 if the cToken does not have a protocolSeizeShareMantissa constant. For example, if the liquidation incentive is 1.08, and the collateral’s seize share is 1.028, liquidators receive an extra 5.2% of the borrower’s collateral for every unit they close, and the remaining 2.8% is added to the cToken’s reserves.
Liquidation incentive can be queried via comptroller.liquidationIncentiveMantissa():
To compute the amount of the collateral that we can liquidate, call comptroller .liquidateCalculateSeizeTokens():
// get amount of collateral to be liquidated
function getAmountToBeLiquidated(
address _cTokenBorrowed,
address _cTokenCollateral,
uint _actualRepayAmount
) external view returns (uint) {
/*
* Get the exchange rate and calculate the number of collateral tokens to seize:
* seizeAmount = actualRepayAmount * liquidationIncentive * priceBorrowed / priceCollateral
* seizeTokens = seizeAmount / exchangeRate
* = actualRepayAmount * (liquidationIncentive * priceBorrowed) / (priceCollateral * exchangeRate)
*/
(uint error, uint cTokenCollateralAmount) = comptroller
.liquidateCalculateSeizeTokens(
_cTokenBorrowed,
_cTokenCollateral,
_actualRepayAmount
);
require(error == 0, "error");
return cTokenCollateralAmount;
}
Finally let's implement the high-level liquidate() function:
// liquidate
function liquidate(
address _borrower,
uint _repayAmount,
address _cTokenCollateral
) external {
// Transfer the fund from user's wallet to this contract
tokenBorrow.transferFrom(msg.sender, address(this), _repayAmount);
// Approve the cTokenBorrow contract to spend this fund
tokenBorrow.approve(address(cTokenBorrow), _repayAmount);
// Call cTokenBorrow.liquidateBorrow() to liquidate
require(
cTokenBorrow.liquidateBorrow(_borrower, _repayAmount, _cTokenCollateral) == 0,
"liquidate failed"
);
}