Skip to content

Commit

Permalink
feat: liquidation protocol fee and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Steven Valeri committed Sep 8, 2021
1 parent 1207d63 commit 1a86b77
Show file tree
Hide file tree
Showing 3 changed files with 637 additions and 24 deletions.
97 changes: 74 additions & 23 deletions contracts/protocol/libraries/logic/LiquidationLogic.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ import {IAToken} from '../../../interfaces/IAToken.sol';
import {IStableDebtToken} from '../../../interfaces/IStableDebtToken.sol';
import {IVariableDebtToken} from '../../../interfaces/IVariableDebtToken.sol';
import {IPriceOracleGetter} from '../../../interfaces/IPriceOracleGetter.sol';
import {
VersionedInitializable
} from '../../libraries/aave-upgradeability/VersionedInitializable.sol';
import {VersionedInitializable} from '../../libraries/aave-upgradeability/VersionedInitializable.sol';
import {ReserveLogic} from '../../libraries/logic/ReserveLogic.sol';
import {Helpers} from '../../libraries/helpers/Helpers.sol';
import {WadRayMath} from '../../libraries/math/WadRayMath.sol';
Expand Down Expand Up @@ -64,13 +62,14 @@ library LiquidationLogic {
uint256 healthFactor;
uint256 liquidatorPreviousATokenBalance;
IAToken collateralAtoken;
ITokenWithTreasury tokenWithTreasury;
IPriceOracleGetter oracle;
bool isCollateralEnabled;
DataTypes.InterestRateMode borrowRateMode;
uint256 errorCode;
string errorMsg;
DataTypes.ReserveCache debtReserveCache;

uint256 liquidationProtocolFeeAmount;
}

/**
Expand All @@ -91,8 +90,10 @@ library LiquidationLogic {
DataTypes.UserConfigurationMap storage userConfig = usersConfig[params.user];
vars.debtReserveCache = debtReserve.cache();


(vars.userStableDebt, vars.userVariableDebt) = Helpers.getUserCurrentDebt(params.user, debtReserve);
(vars.userStableDebt, vars.userVariableDebt) = Helpers.getUserCurrentDebt(
params.user,
debtReserve
);
vars.oracle = IPriceOracleGetter(params.priceOracle);

ValidationLogic.validateLiquidationCall(
Expand All @@ -108,7 +109,6 @@ library LiquidationLogic {
);

vars.collateralAtoken = IAToken(collateralReserve.aTokenAddress);

vars.userCollateralBalance = vars.collateralAtoken.balanceOf(params.user);

vars.maxLiquidatableDebt = (vars.userStableDebt + vars.userVariableDebt).percentMul(
Expand All @@ -121,7 +121,8 @@ library LiquidationLogic {

(
vars.maxCollateralToLiquidate,
vars.debtAmountNeeded
vars.debtAmountNeeded,
vars.liquidationProtocolFeeAmount
) = _calculateAvailableCollateralToLiquidate(
collateralReserve,
vars.debtReserveCache,
Expand Down Expand Up @@ -149,7 +150,12 @@ library LiquidationLogic {
vars.debtReserveCache.nextVariableBorrowIndex
);
vars.debtReserveCache.refreshDebt(0, 0, 0, vars.actualDebtToLiquidate);
debtReserve.updateInterestRates(vars.debtReserveCache, params.debtAsset, vars.actualDebtToLiquidate, 0);
debtReserve.updateInterestRates(
vars.debtReserveCache,
params.debtAsset,
vars.actualDebtToLiquidate,
0
);
} else {
// If the user doesn't have variable debt, no need to try to burn variable debt tokens
if (vars.userVariableDebt > 0) {
Expand All @@ -170,12 +176,21 @@ library LiquidationLogic {
vars.userVariableDebt
);

debtReserve.updateInterestRates(vars.debtReserveCache, params.debtAsset, vars.actualDebtToLiquidate, 0);
debtReserve.updateInterestRates(
vars.debtReserveCache,
params.debtAsset,
vars.actualDebtToLiquidate,
0
);
}

if (params.receiveAToken) {
vars.liquidatorPreviousATokenBalance = IERC20(vars.collateralAtoken).balanceOf(msg.sender);
vars.collateralAtoken.transferOnLiquidation(params.user, msg.sender, vars.maxCollateralToLiquidate);
vars.collateralAtoken.transferOnLiquidation(
params.user,
msg.sender,
vars.maxCollateralToLiquidate
);

if (vars.liquidatorPreviousATokenBalance == 0) {
DataTypes.UserConfigurationMap storage liquidatorConfig = usersConfig[msg.sender];
Expand All @@ -201,6 +216,16 @@ library LiquidationLogic {
);
}

// Transfer fee to treasury if it is non-zero
if (vars.liquidationProtocolFeeAmount > 0) {
vars.tokenWithTreasury = ITokenWithTreasury(collateralReserve.aTokenAddress);
vars.collateralAtoken.transferOnLiquidation(
params.user,
vars.tokenWithTreasury.RESERVE_TREASURY_ADDRESS(),
vars.liquidationProtocolFeeAmount
);
}

// If the collateral being liquidated is equal to the user balance,
// we set the currency as not being used as collateral anymore
if (vars.maxCollateralToLiquidate == vars.userCollateralBalance) {
Expand Down Expand Up @@ -236,6 +261,10 @@ library LiquidationLogic {
uint256 collateralDecimals;
uint256 collateralAssetUnit;
uint256 debtAssetUnit;
uint256 collateralAmount;
uint256 debtAmountNeeded;
uint256 liquidationProtocolFeePercentage;
uint256 liquidationProtocolFeeAmount;
}

/**
Expand All @@ -261,10 +290,15 @@ library LiquidationLogic {
uint256 debtToCover,
uint256 userCollateralBalance,
IPriceOracleGetter oracle
) internal view returns (uint256, uint256) {
uint256 collateralAmount = 0;
uint256 debtAmountNeeded = 0;

)
internal
view
returns (
uint256,
uint256,
uint256
)
{
AvailableCollateralToLiquidateLocalVars memory vars;

vars.collateralPrice = oracle.getAssetPrice(collateralAsset);
Expand All @@ -273,13 +307,16 @@ library LiquidationLogic {
(, , vars.liquidationBonus, vars.collateralDecimals, ) = collateralReserve
.configuration
.getParams();

vars.debtAssetDecimals = debtReserveCache.reserveConfiguration.getDecimalsMemory();
unchecked {
vars.collateralAssetUnit = 10**vars.collateralDecimals;
vars.debtAssetUnit = 10**vars.debtAssetDecimals;
}

vars.liquidationProtocolFeePercentage = collateralReserve
.configuration
.getLiquidationProtocolFee();

// This is the maximum possible amount of the selected collateral that can be liquidated, given the
// max amount of liquidatable debt
vars.maxAmountCollateralToLiquidate =
Expand All @@ -291,14 +328,28 @@ library LiquidationLogic {
(vars.collateralPrice * vars.debtAssetUnit);

if (vars.maxAmountCollateralToLiquidate > userCollateralBalance) {
collateralAmount = userCollateralBalance;
debtAmountNeeded = ((vars.collateralPrice * collateralAmount * vars.debtAssetUnit) /
(vars.debtAssetPrice * vars.collateralAssetUnit))
.percentDiv(vars.liquidationBonus);
vars.collateralAmount = userCollateralBalance;
vars.debtAmountNeeded = ((vars.collateralPrice * vars.collateralAmount * vars.debtAssetUnit) /
(vars.debtAssetPrice * vars.collateralAssetUnit)).percentDiv(vars.liquidationBonus);
} else {
collateralAmount = vars.maxAmountCollateralToLiquidate;
debtAmountNeeded = debtToCover;
vars.collateralAmount = vars.maxAmountCollateralToLiquidate;
vars.debtAmountNeeded = debtToCover;
}
return (collateralAmount, debtAmountNeeded);

if (vars.liquidationProtocolFeePercentage > 0) {
vars.liquidationProtocolFeeAmount = vars.collateralAmount.percentMul(
vars.liquidationProtocolFeePercentage
);
return (
vars.collateralAmount - vars.liquidationProtocolFeeAmount,
vars.debtAmountNeeded,
vars.liquidationProtocolFeeAmount
);
}
return (vars.collateralAmount, vars.debtAmountNeeded, 0);
}
}

interface ITokenWithTreasury {
function RESERVE_TREASURY_ADDRESS() external view returns (address);
}
2 changes: 1 addition & 1 deletion test-suites/liquidation-underlying.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ makeSuite('Pool Liquidation: Liquidator receiving the underlying asset', (testEn
.connect(liquidator.signer)
.mint(await convertToCurrencyDecimals(usdc.address, '1000'));

//approve protocol to access depositor wallet
//approve protocol to access liquidator wallet
await usdc.connect(liquidator.signer).approve(pool.address, MAX_UINT_AMOUNT);

const userReserveDataBefore = await helpersContract.getUserReserveData(
Expand Down
Loading

0 comments on commit 1a86b77

Please sign in to comment.