when validateOffer(), if principalAmount > currentBalance , we need to get back enough from getBaseInterestAllocator to avoid not having enough balance to pay the offer.
function validateOffer(bytes calldata _offer, uint256 _protocolFee) external override onlyAcceptedCallers {
if (!isActive) {
revert PoolStatusError();
}
uint256 currentBalance = asset.balanceOf(address(this)) - getAvailableToWithdraw;
uint256 baseRateBalance = IBaseInterestAllocator(getBaseInterestAllocator).getAssetsAllocated();
uint256 undeployedAssets = currentBalance + baseRateBalance;
(uint256 principalAmount, uint256 apr) = IPoolOfferHandler(getUnderwriter).validateOffer(
IBaseInterestAllocator(getBaseInterestAllocator).getBaseAprWithUpdate(), _offer
);
/// @dev Since the balance of the pool includes capital that is waiting to be claimed by the queues,
/// we need to check if the pool has enough capital to fund the loan.
/// If that's not the case, and the principal is larger than the currentBalance, the we need to reallocate
/// part of it.
if (principalAmount > undeployedAssets) {
revert InsufficientAssetsError();
} else if (principalAmount > currentBalance) {
IBaseInterestAllocator(getBaseInterestAllocator).reallocate(
@> currentBalance, principalAmount - currentBalance, true
);
}
/// @dev If the txn doesn't revert, we can assume the loan was executed.
_outstandingValues = _getNewLoanAccounting(principalAmount, _netApr(apr, _protocolFee));
}
The above call to IBaseInterestAllocator(getBaseInterestAllocator).reallocate(_currentIdle, _targetIdle) with the second argument _targetIdle is wrong.
Instead of passing _targetIdle = principalAmount - currentBalance, we should pass _targetIdle = principalAmount directly
The reallocate() method calculates the delta itself internally.
reallocate() incorrectly passes delta, which may cause withdraw to become deposit, resulting in insufficient balance and failed offer.
Recommended Mitigation
function validateOffer(bytes calldata _offer, uint256 _protocolFee) external override onlyAcceptedCallers {
if (!isActive) {
revert PoolStatusError();
}
uint256 currentBalance = asset.balanceOf(address(this)) - getAvailableToWithdraw;
uint256 baseRateBalance = IBaseInterestAllocator(getBaseInterestAllocator).getAssetsAllocated();
uint256 undeployedAssets = currentBalance + baseRateBalance;
(uint256 principalAmount, uint256 apr) = IPoolOfferHandler(getUnderwriter).validateOffer(
IBaseInterestAllocator(getBaseInterestAllocator).getBaseAprWithUpdate(), _offer
);
/// @dev Since the balance of the pool includes capital that is waiting to be claimed by the queues,
/// we need to check if the pool has enough capital to fund the loan.
/// If that's not the case, and the principal is larger than the currentBalance, the we need to reallocate
/// part of it.
if (principalAmount > undeployedAssets) {
revert InsufficientAssetsError();
} else if (principalAmount > currentBalance) {
IBaseInterestAllocator(getBaseInterestAllocator).reallocate(
- currentBalance, principalAmount - currentBalance, true
+ currentBalance, principalAmount, true
);
}
/// @dev If the txn doesn't revert, we can assume the loan was executed.
_outstandingValues = _getNewLoanAccounting(principalAmount, _netApr(apr, _protocolFee));
}
Lines of code
https://github.com/code-423n4/2024-04-gondi/blob/b9863d73c08fcdd2337dc80a8b5e0917e18b036c/src/lib/pools/Pool.sol#L411
Vulnerability details
Vulnerability details
when
validateOffer()
, if principalAmount > currentBalance , we need to get back enough fromgetBaseInterestAllocator
to avoid not having enough balance to pay theoffer
.The above call to
IBaseInterestAllocator(getBaseInterestAllocator).reallocate(_currentIdle, _targetIdle)
with the second argument_targetIdle
is wrong. Instead of passing_targetIdle = principalAmount - currentBalance
, we should pass_targetIdle = principalAmount
directlyThe
reallocate()
method calculates thedelta
itself internally.Take
AaveUsdcBaseInterestAllocator
as an example:Impact
reallocate()
incorrectly passesdelta
, which may causewithdraw
to becomedeposit
, resulting in insufficient balance and failedoffer
.Recommended Mitigation
Assessed type
Context