Summary
The Kitty-Fi contract is potentially vulnerable to front-running attacks, where malicious actors can exploit the timing of transactions to gain an unfair advantage.
Vulnerability Details
A front-running attack occurs when a malicious actor observes a pending transaction in the mempool and submits a similar transaction with a higher gas fee to be processed before the original transaction. This can be particularly problematic in DeFi contracts where timing and order of transactions are critical.
The executeDepawsit
executeWhiskdrawal
amd purrgeBadPawsition
functions does not have mechanism to prevent front-running attack.
function executeDepawsit(address _user, uint256 _ameownt) external onlyPool {
uint256 _totalMeowllateral = getTotalMeowllateral();
uint256 _cattyNipGenerated;
if (_totalMeowllateral == 0) {
_cattyNipGenerated = _ameownt;
}
else {
>> _cattyNipGenerated = _ameownt.mulDiv(totalCattyNip, _totalMeowllateral);
}
userToCattyNip[_user] += _cattyNipGenerated;
totalCattyNip += _cattyNipGenerated;
totalMeowllateralInVault += _ameownt;
IERC20(i_token).safeTransferFrom(_user, address(this), _ameownt);
}
A malicious actor could front-run a deposit transaction to manipulate the user's collateral status.
function executeWhiskdrawal(address _user, uint256 _cattyNipToWithdraw) external onlyPool {
>> uint256 _ameownt = _cattyNipToWithdraw.mulDiv(getTotalMeowllateral(), totalCattyNip);
userToCattyNip[_user] -= _cattyNipToWithdraw;
totalCattyNip -= _cattyNipToWithdraw;
totalMeowllateralInVault -= _ameownt;
IERC20(i_token).safeTransfer(_user, _ameownt);
}
A malicious actor could front-run a withdrawal transaction to manipulate the user's collateral status or liquidate their position.
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;
}
}
}
A malicious actor could front-run the liquidation transaction to manipulate the user's debt position or collateral status, potentially profiting from the liquidation process.
Impact
Users could suffer financial losses if their transactions are front-run by malicious actors. For example, a user attempting to deposit
or withdraw
collateral might find their transaction executed at a less favorable rate or even reverted.
In the case of purrgeBadPawsition
, a malicious actor could front-run the liquidation process to profit from the user's collateral.
Tools used
Manual Review
Recommendations
Implement a commit-reveal scheme for sensitive operations to prevent front-running. Users first commit to a transaction by submitting a hash
, and later reveal the details to execute it.