purrgeBadPawsition(KittyPool.sol) mistakenly use VaultMeowllateralInEuros instead of VaultMeowllateral amount, which leads to the wrong vaultCollateral for the following steps, then can't execuse purrgeBadPawsition successfully.
The liquidation logic firstly is to calculate the redeemPercent of the user's vaultCollateral based on the current collateral percent(<169)
Then calcuating how much collateral token to the liquidator, should based on the liquidated user's collateral amount, not the collateral euro value.
If based on the collateral euro value, the vaultCollateral value will greater than the actual value, Then calling the executeWhiskdrawal function will beyond liquidator's cattyNip(share).
* @notice Liquidates the bad debt position of the user
* @param _user address of the user
*/
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;
}
}
}
The protocol can't guarantee the kittyCoin's price, such as when user's collateral value under the requirements. No ways to matain the totoal collateral value.
function test_poc_purrgeBadPawsition() public {
kittyPool = new KittyPool(
meowntainer,
config.euroPriceFeed,
config.aavePool
);
kittyCoin = KittyCoin(kittyPool.getKittyCoin());
ERC20Mock mockWethToken = new ERC20Mock();
MockV3Aggregator wethPriceFeed = new MockV3Aggregator(8, 20000e8);
vm.prank(meowntainer);
kittyPool.meownufactureKittyVault(
address(mockWethToken),
address(wethPriceFeed)
);
KittyVault wethMockVault = KittyVault(
kittyPool.getTokenToVault(address(mockWethToken))
);
address user1 = makeAddr("user1");
AMOUNT = 1 ether;
mockWethToken.mint(user1, AMOUNT);
uint256 toDeposit = 1 ether;
vm.startPrank(user1);
mockWethToken.approve(address(wethMockVault), toDeposit);
kittyPool.depawsitMeowllateral(address(mockWethToken), toDeposit);
uint256 amountToMint = getAlmostMaxKittyTokenForMockwethVault(
wethMockVault,
user1
);
kittyPool.meowintKittyCoin(amountToMint);
vm.stopPrank();
address liquidator = makeAddr("liquidator");
mockWethToken.mint(liquidator, AMOUNT);
vm.startPrank(liquidator);
mockWethToken.approve(address(wethMockVault), toDeposit);
kittyPool.depawsitMeowllateral(address(mockWethToken), toDeposit);
kittyPool.meowintKittyCoin(amountToMint);
vm.stopPrank();
console.log("When ETH price 2000");
console.log("user1 collateral info........................start");
uint256 user1VaultMeowllateralInEuros = wethMockVault
.getUserVaultMeowllateralInEuros(user1);
console.log("collllaterl Vaule in EUR", user1VaultMeowllateralInEuros);
uint256 user1lDebtForKittyCoins = kittyCoin.balanceOf(user1);
console.log("kittyCoins", user1lDebtForKittyCoins);
console.log(
"user1 collateral_percent",
(user1VaultMeowllateralInEuros * 100) / user1lDebtForKittyCoins
);
console.log("user1 collateral info........................end");
wethPriceFeed.updateAnswer(12000e8);
console.log("Change ETH price 2000=>1200");
console.log(
"Show user1's current collateral percent...................start"
);
user1VaultMeowllateralInEuros = wethMockVault
.getUserVaultMeowllateralInEuros(user1);
console.log("collllaterl Vaule in EUR", user1VaultMeowllateralInEuros);
user1lDebtForKittyCoins = kittyCoin.balanceOf(user1);
console.log("kittyCoins", user1lDebtForKittyCoins);
console.log(
"user1 collateral_percent",
(user1VaultMeowllateralInEuros * 100) / user1lDebtForKittyCoins
);
console.log(
"Show user1's current collateral percent...................end"
);
console.log(
"liquidator weth balance",
mockWethToken.balanceOf(liquidator)
);
console.log(
"liquidator kittyCoins balance",
kittyCoin.balanceOf(liquidator)
);
vm.startPrank(liquidator);
kittyPool.purrgeBadPawsition(user1);
vm.stopPrank();
console.log("after liquuidation");
console.log(
"liquidator weth balance",
mockWethToken.balanceOf(liquidator)
);
console.log(
"liquidator kittyCoins balance",
kittyCoin.balanceOf(liquidator)
);
}
function getAlmostMaxKittyTokenForMockwethVault(
KittyVault wethMockVault,
address user
) internal view returns (uint256) {
return
(wethMockVault.getUserVaultMeowllateralInEuros(user) * 100) /
169 -
1e18;
}
interface IKittyVault {
function getUserVaultMeowllateralInEuros(
address _user
) external view returns (uint256);
function executeDepawsit(address _user, uint256 _ameownt) external;
function executeWhiskdrawal(address _user, uint256 _ameownt) external;
function getUserMeowllateral(address _user) external view returns (uint256);
}
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.getUserMeowllateral(_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;
}
}
}