You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Liquidation fee is not taken from user's collateral balance & underlying balance
Summary
Liquidation fee is not taken from user's collateral balance during liquidation as a result the user can withdraw their remaining collateral (including the liquidation fee) , causing insolvency issues for other users yet to withdraw their deposit.
function executeLiquidationCall(
mapping(address=> DataTypes.ReserveData) storagereservesData,
mapping(uint256=>address) storagereservesList,
mapping(address=>mapping(bytes32=> DataTypes.PositionBalance)) storagebalances,
mapping(address=> DataTypes.ReserveSupplies) storagetotalSupplies,
mapping(bytes32=> DataTypes.UserConfigurationMap) storageusersConfig,
DataTypes.ExecuteLiquidationCallParams memoryparams
) external {
//...// Transfer fee to treasury if it is non-zeroif (vars.liquidationProtocolFeeAmount !=0) {
uint256 liquidityIndex = collateralReserve.getNormalizedIncome();
uint256 scaledDownLiquidationProtocolFee = vars.liquidationProtocolFeeAmount.rayDiv(liquidityIndex);
uint256 scaledDownUserBalance = balances[params.collateralAsset][params.position].supplyShares;
if (scaledDownLiquidationProtocolFee > scaledDownUserBalance) {
vars.liquidationProtocolFeeAmount = scaledDownUserBalance.rayMul(liquidityIndex);
}
IERC20(params.collateralAsset).safeTransfer(IPool(params.pool).factory().treasury(), vars.liquidationProtocolFeeAmount);
}
// Transfers the debt asset being repaid to the aToken, where the liquidity is keptIERC20(params.debtAsset).safeTransferFrom(msg.sender, address(params.pool), vars.actualDebtToLiquidate);
emit PoolEventsLib.LiquidationCall(
params.collateralAsset, params.debtAsset, params.position, vars.actualDebtToLiquidate, vars.actualCollateralToLiquidate, msg.sender
);
}
We can see that the liquidation amount is capped by the user's supply shares (this is to prevent charging other users) ,however the liquidation amount is not taken from the user's collateral balance which means they can still all their remaining collateral including the liquidation fee , effectively charging other users the liquidation amount.
Internal pre-conditions
No response
External pre-conditions
No response
Attack Path
No response
Impact
Debtors are not charged the liquidation fee , resulting in insolvency as the totalShares(x liquidityIndex) will be greater than the available asset in the pool
PoC
No response
Mitigation
function executeLiquidationCall(
mapping(address => DataTypes.ReserveData) storage reservesData,
mapping(uint256 => address) storage reservesList,
mapping(address => mapping(bytes32 => DataTypes.PositionBalance)) storage balances,
mapping(address => DataTypes.ReserveSupplies) storage totalSupplies,
mapping(bytes32 => DataTypes.UserConfigurationMap) storage usersConfig,
DataTypes.ExecuteLiquidationCallParams memory params
) external {
//...
// Transfer fee to treasury if it is non-zero
if (vars.liquidationProtocolFeeAmount != 0) {
uint256 liquidityIndex = collateralReserve.getNormalizedIncome();
uint256 scaledDownLiquidationProtocolFee = vars.liquidationProtocolFeeAmount.rayDiv(liquidityIndex);
uint256 scaledDownUserBalance = balances[params.collateralAsset][params.position].supplyShares;
if (scaledDownLiquidationProtocolFee > scaledDownUserBalance) {
+ scaledDownLiquidationProtocolFee = scaledDownUserBalance;
vars.liquidationProtocolFeeAmount = scaledDownUserBalance.rayMul(liquidityIndex);
}
+ balances[params.collateralAsset][params.position].supplyShares -= scaledDownLiquidationProtocolFee;+ totalSupplies[params.collateralAsset].underlyingBalance -= vars.liquidationProtocolFeeAmount;
IERC20(params.collateralAsset).safeTransfer(IPool(params.pool).factory().treasury(), vars.liquidationProtocolFeeAmount);
}
// Transfers the debt asset being repaid to the aToken, where the liquidity is kept
IERC20(params.debtAsset).safeTransferFrom(msg.sender, address(params.pool), vars.actualDebtToLiquidate);
emit PoolEventsLib.LiquidationCall(
params.collateralAsset, params.debtAsset, params.position, vars.actualDebtToLiquidate, vars.actualCollateralToLiquidate, msg.sender
);
}
sherlock-admin3
changed the title
Shiny Daisy Osprey - Liquidation fee is not taken from user's collateral balance & underlying balance
Honour - Liquidation fee is not taken from user's collateral balance & underlying balance
Oct 3, 2024
Honour
High
Liquidation fee is not taken from user's collateral balance & underlying balance
Summary
Liquidation fee is not taken from user's collateral balance during liquidation as a result the user can withdraw their remaining collateral (including the liquidation fee) , causing insolvency issues for other users yet to withdraw their deposit.
Root Cause
In
LiquidationLogic::executeLiquidationCall
https://github.com/sherlock-audit/2024-06-new-scope/blob/main/zerolend-one/contracts/core/pool/logic/LiquidationLogic.sol#L178-L189
We can see that the liquidation amount is capped by the user's supply shares (this is to prevent charging other users) ,however the liquidation amount is not taken from the user's collateral balance which means they can still all their remaining collateral including the liquidation fee , effectively charging other users the liquidation amount.
Internal pre-conditions
No response
External pre-conditions
No response
Attack Path
No response
Impact
Debtors are not charged the liquidation fee , resulting in insolvency as the totalShares(x liquidityIndex) will be greater than the available asset in the pool
PoC
No response
Mitigation
Duplicate of #228
The text was updated successfully, but these errors were encountered: