Open hats-bug-reporter[bot] opened 5 months ago
reference of _toShare
function :
function _toShares(
uint256 amount,
uint256 totalShares_,
uint256 totalAmount,
bool roundUp
) internal pure returns (uint256 share) {
// To prevent reseting the ratio due to withdrawal of all shares, we start with
// 1 amount/1e8 shares already burned. This also starts with a 1 : 1e8 ratio which
// functions like 8 decimal fixed point math. This prevents ratio attacks or inaccuracy
// due to 'gifting' or rebasing tokens. (Up to a certain degree)
totalAmount++;
totalShares_ += 1e8;
// Calculte the shares using te current amount to share ratio
share = (amount * totalShares_) / totalAmount;
// Default is to round down (Solidity), round up if required
if (roundUp && (share * totalAmount) / totalShares_ < amount) {
share++;
}
}
collateralShare
is borrowAmount + bonus. Rounding up borrowAmount
when is converted to shares doesn't make it higher than collateralShare
Hi @cryptotechmaker I will disagree with you respectfully on following points:
borrowAmount
is calculated :
Tapioca--Lending-Engine--0x5bee198f5b060eecd86b299fdbea6b0c07c728dd/contracts/markets/bigBang/BBLiquidation.sol:203
203: borrowPartWithBonus = borrowPartWithBonus > maxBorrowPart ? maxBorrowPart : borrowPartWithBonus;
204: borrowAmount = borrowPartWithBonus;
205:
The borrowPartWithBonus
will only be update in case the market has set liquidationBonusAmount
other wise the borrowAmount
and borrowPartWithBonus
.
Tapioca--Lending-Engine--0x5bee198f5b060eecd86b299fdbea6b0c07c728dd/contracts/markets/bigBang/BBLiquidation.sol:212
212: if (liquidationBonusAmount > 0) {
213: borrowPartWithBonus = borrowPartWithBonus + (borrowPartWithBonus * liquidationBonusAmount) / FEE_PRECISION;
214: }
Here We can assume that there are certain case or markets on which the borrowAmount
and borrowPartWithBonus
will be same , even in cases where the liquidationBonusAmount
is 1e2
the borrowPartWithBonus
will be increase by 1
. More on this point in POC.
collateralShare
is calcualted :
Assume here that collateralPartInAsset >= borrowPartWithBonus
so the collateralShare
is :
Tapioca--Lending-Engine--0x5bee198f5b060eecd86b299fdbea6b0c07c728dd/contracts/markets/bigBang/BBLiquidation.sol:234
234: collateralShare =
235: yieldBox.toShare(collateralId, (borrowPartWithBonus * _exchangeRate) / EXCHANGE_RATE_PRECISION, false); // @audit : also round down here
So now Lets put all above points together with some POC values:
borrowAmount = 1051 // code snippet 1 both are smae
borrowWithBonus = 1052; // = liquidationBonusAmount = 1e2 => 1051 + 1=> 1052 , even it is 1e1 =>1051 + (1051 * 1e1)/1e55 => 1051 + 0.1051 . if we have `liquidationBonusAmount>0` then borrowWithBonus will be updated
borrowWithBonus=1052
collateralShare = 1051// due to round down it return 1051 in this case assume exchangeRate => 1 ether / 1e18 => 1
So now we have :
collateralShare = 1051
borrowAmount = 1051
borrowShare = 1052 // due to round up
uint256 borrowShare = yieldBox.toShare(assetId, borrowAmount, true); // @audit : round up
// _swapCollateralWithAsset function will return returnedShare which we have round down
returnedShare = 1051
returnedShare = yieldBox.toShare(assetId, returnedAmount, false); // @audit : no rounding
if (returnedShare < borrowShare) revert AmountNotValid(); // borrowShare = 1052 , && returndShare = 1051 it will revert
The issue would occur very frequently where the Bonus Fee is 0>=liquidationBonusAmount<1e2
. I would suggest to remove the roundup where the borrowShare
calculated or add roundup where the collateralShare
is calculated.
One more Point to keep in mind is that we do allow to liquidate the user when returndShare=borrowShare
So the above POC is valid according to code logic.
I think you're presenting a hypothetical case here, because:
liquidate
borrowAmount = 1051
and collateralShare = 1051
is possible only if the position is not liquidated at the right time; it would also revert with BadDebt
error since if (collateralPartInAsset < borrowPartWithBonus) revert BadDebt();
999999999999999999996484125
vs 999999999999999999996484126
)borrowAmount = 1051 and collateralShare = 1051 is possible only if the position is not liquidated at the right time; it would also revert with BadDebt error since if (collateralPartInAsset < borrowPartWithBonus) revert BadDebt();
collateralShare
is calculated in else case so the BadDebt()
would not apply . and collateralPartInAsset < borrowPartWithBonus
is not true because both are equal. returnedShare
value is result of two round down 1 here ,then we pass the collateralShare
value for swap and round down returnedShare
2nd here after swap. Note: I have perform all the calculation without decimals. Exactly the 1/2 wei difference will not allow the liquidator to liquidate due to wrong rounding.
Github username: -- Twitter username: -- Submission hash (on-chain): 0x5b66cf2b8f130df2a37362a32438f5ba2b12c8d078527a7ced1b08f7d99d4b40 Severity: medium
Description: Description\ Describe the context and the effect of the vulnerability.
Attack Scenario\ Describe how the vulnerability can be exploited.
Attachments**Description**\ The
BBLiquidation:_liquidateUser
will revert due to wrong casting ofborrowShare
andreturnedShare
. When we calculate theborrowShare
of assets we round up the share in favor of caller. https://github.com/hats-finance/Tapioca--Lending-Engine--0x5bee198f5b060eecd86b299fdbea6b0c07c728dd/blob/dev/contracts/markets/bigBang/BBLiquidation.sol#L277-L282Inside this
_swapCollateralWithAsset
function We do not round up:https://github.com/hats-finance/Tapioca--Lending-Engine--0x5bee198f5b060eecd86b299fdbea6b0c07c728dd/blob/dev/contracts/markets/bigBang/BBLiquidation.sol#L174-L175
Due to this rounding issue The User will not be able to liquidate the borrower who has created bad bedt in the market. However The owner can still liquidate this user because in case of owner liquidation calls we never round up. That why it is medium.
Attack Scenario\ Let assumes that Bob calls liquidate function to Liquidate Alice.
3.4
we round up it will return4
. So theborrowShare=4
3
Because there is no rounding up. So thereturnedShare=3
if (returnedShare < borrowShare) revert AmountNotValid();
becausereturnShare=3
andborrowShare=4
.Matigation\ Either don't perform rounding up in either case or perform it in all cases. It can also be fix if we keep consistency by passing an Flag to
_swapCollateralWithAsset
which will be passed_toShare
function in both function_swapCollateralWithAsset
and_liquidateUser
. this will it will not impact the owner logic