[L-01] the KittyPool::purrgeBadPawsition
wont work with revert on zero transfer tokens
Summary
the purrgeBadPawsition
will loop through vaults in order to liquidate user based on how much he owns in each vault. if a user has no collateral in one of the vaults it wont cause any problems, but if one of the collateral tokens is a wierd erc20 which reverts on zero transfer amount, it will revert the whole liqudation call, as a result users can just leave that specific collateral empty to never get liquidated.
Vulnerability Details
Note that if the vaultCollateral
is zero, there is no check and it will try to call the executeWhiskdrawal nevertheless. causing reverts if there is a revert on zero transfer token in collaterals.
function purrgeBadPawsition(
address _user
) external returns (uint256 _totalAmountReceived) {
require(!(_hasEnoughMeowllateral(_user)), KittyPool__UserIsPurrfect());
uint256 totalDebt = kittyCoinMeownted[_user];
kittyCoinMeownted[_user] = 0;
i_kittyCoin.burn(msg.sender, totalDebt);
uint256 userMeowllateralInEuros = getUserMeowllateralInEuros(_user);
uint256 redeemPercent;
if (totalDebt >= userMeowllateralInEuros) {
redeemPercent = PRECISION;
} else {
redeemPercent = totalDebt.mulDiv(
PRECISION,
userMeowllateralInEuros
);
}
uint256 vaults_length = vaults.length;
for (uint256 i; i < vaults_length; ) {
IKittyVault _vault = IKittyVault(vaults[i]);
@> uint256 vaultCollateral = _vault.getUserVaultMeowllateralInEuros(
_user
);
uint256 toDistribute = vaultCollateral.mulDiv(
redeemPercent,
PRECISION
);
uint256 extraCollateral = vaultCollateral - toDistribute;
uint256 extraReward = toDistribute.mulDiv(
REWARD_PERCENT,
PRECISION
);
extraReward = Math.min(extraReward, extraCollateral);
_totalAmountReceived += (toDistribute + extraReward);
@> _vault.executeWhiskdrawal(msg.sender, toDistribute + extraReward);
unchecked {
++i;
}
}
}
Impact
Users can manipulate this to stop themselves from getting liquidated.
Tools Used
Manual Review
Recommendations
add a check before executeWhiskdrawal call to check if the amount being transfered is zero or not, if its zero dont transfer.
function purrgeBadPawsition(
address _user
) external returns (uint256 _totalAmountReceived) {
require(!(_hasEnoughMeowllateral(_user)), KittyPool__UserIsPurrfect());
uint256 totalDebt = kittyCoinMeownted[_user];
kittyCoinMeownted[_user] = 0;
i_kittyCoin.burn(msg.sender, totalDebt);
uint256 userMeowllateralInEuros = getUserMeowllateralInEuros(_user);
uint256 redeemPercent;
if (totalDebt >= userMeowllateralInEuros) {
redeemPercent = PRECISION;
} else {
redeemPercent = totalDebt.mulDiv(
PRECISION,
userMeowllateralInEuros
);
}
uint256 vaults_length = vaults.length;
for (uint256 i; i < vaults_length; ) {
IKittyVault _vault = IKittyVault(vaults[i]);
uint256 vaultCollateral = _vault.getUserVaultMeowllateralInEuros(
_user
);
uint256 toDistribute = vaultCollateral.mulDiv(
redeemPercent,
PRECISION
);
uint256 extraCollateral = vaultCollateral - toDistribute;
uint256 extraReward = toDistribute.mulDiv(
REWARD_PERCENT,
PRECISION
);
extraReward = Math.min(extraReward, extraCollateral);
_totalAmountReceived += (toDistribute + extraReward);
+ if ((toDistribute + extraReward) != 0){
+ _vault.executeWhiskdrawal(msg.sender, toDistribute + extraReward);
+ }
- _vault.executeWhiskdrawal(msg.sender, toDistribute + extraReward);
unchecked {
++i;
}
}
}