-
Notifications
You must be signed in to change notification settings - Fork 4
Closed
Labels
2 (Med Risk)Assets not at direct risk, but function/availability of the protocol could be impacted or leak valueAssets not at direct risk, but function/availability of the protocol could be impacted or leak value🤖_11_groupAI based duplicate group recommendationAI based duplicate group recommendationbugSomething isn't workingSomething isn't workingdowngraded by judgeJudge downgraded the risk level of this issueJudge downgraded the risk level of this issueduplicate-11satisfactorysatisfies C4 submission criteria; eligible for awardssatisfies C4 submission criteria; eligible for awards
Description
Lines of code
Vulnerability details
Vulnerability details
in BalancerFlashLender.flashFee()
Calculated using round down
function flashFee(address, uint256 amount) external view override returns (uint256) {
uint256 perc = _balancerVault.getProtocolFeesCollector().getFlashLoanFeePercentage();
if (perc == 0 || amount == 0) {
return 0;
}
@> return (amount * perc) / _BALANCER_MAX_FEE_PERCENTAGE;
}But balancer is used `round up'.
* @dev Returns the protocol fee amount to charge for a flash loan of `amount`.
*/
function _calculateFlashLoanFeeAmount(uint256 amount) internal view returns (uint256) {
// Fixed point multiplication introduces error: we round up, which means in certain scenarios the charged
// percentage can be slightly higher than intended.
uint256 percentage = getProtocolFeesCollector().getFlashLoanFeePercentage();
@> return FixedPoint.mulUp(amount, percentage);
}This way when we set the allowance in StrategyLeverage, it may be 1 wei smaller than it actually needs to be.
Take deploy() for example
function deploy() external payable onlyOwner nonReentrant returns (uint256 deployedAmount) {
if (msg.value == 0) revert InvalidDeployAmount();
// 1. Wrap Ethereum
address(wETHA()).functionCallWithValue(abi.encodeWithSignature("deposit()"), msg.value);
// 2. Initiate a WETH Flash Loan
uint256 leverage = calculateLeverageRatio(
msg.value,
getLoanToValue(),
getNrLoops()
);
uint256 loanAmount = leverage - msg.value;
@> uint256 fee = flashLender().flashFee(wETHA(), loanAmount);
//§uint256 allowance = wETH().allowance(address(this), flashLenderA());
@> if(!wETH().approve(flashLenderA(), loanAmount + fee)) revert FailedToApproveAllowance();
if (
!flashLender().flashLoan(
IERC3156FlashBorrowerUpgradeable(this),
wETHA(),
loanAmount,
abi.encode(msg.value, msg.sender, FlashLoanAction.SUPPLY_BOORROW)
)
) {
revert FailedToRunFlashLoan();
}
...Finally receiveFlashLoan()check allowances or balancer will fail at the end of the flash loan when getting the fee due to insufficient allowances leading to revert, which will cause all methods relying on the flash loan to fail
Impact
A flash loan will cause several core functions of the protocol to fail, such as valult's deposit/withdraw/rebalance.
Recommended Mitigation
Use round up for flashFee() calculations
Assessed type
Context
Metadata
Metadata
Assignees
Labels
2 (Med Risk)Assets not at direct risk, but function/availability of the protocol could be impacted or leak valueAssets not at direct risk, but function/availability of the protocol could be impacted or leak value🤖_11_groupAI based duplicate group recommendationAI based duplicate group recommendationbugSomething isn't workingSomething isn't workingdowngraded by judgeJudge downgraded the risk level of this issueJudge downgraded the risk level of this issueduplicate-11satisfactorysatisfies C4 submission criteria; eligible for awardssatisfies C4 submission criteria; eligible for awards