Possible fee leeching
Description
The earn
function in VaultV2 takes the entire available balance of the want token and deposits it into the strategy, for accruing yield.
function earn() public override {
if (_isStratActive()) {
uint256 balanceAvail = available();
TransferHelper.safeTransfer(want, address(strategy), balanceAvail);
strategy.deposit();
}
}
The deposit of all the available balance, however, might hinder the ability of users to withdraw want tokens from the vault. In order for a user withdrawal to occur, the Vault must have enough want tokens in the first place. This means that the Vault must withdraw additional tokens from the strategy in order to fulfill the user's withdrawal request.
function withdraw(uint256 shares) public override nonReentrant {
// ...
uint256 balanceBefore = IERC20(want).balanceOf(address(this));
if (balanceBefore < withdrawAmount) {
uint256 balanceToWithdraw = withdrawAmount - balanceBefore;
require(_isStratActive(), "WOOFiVaultV2: STRAT_INACTIVE");
strategy.withdraw(balanceToWithdraw);
// ...
}
}
In turn, this withdraw incurs additional fees, which are not accounted for in the Vault's accounting.
function withdraw(uint256 amount) public override nonReentrant {
require(msg.sender == vault, "StrategyAave: !vault");
require(amount > 0, "StrategyAave: !amount");
uint256 wantBal = balanceOfWant();
if (wantBal < amount) {
IAavePool(aavePool).withdraw(want, amount - wantBal, address(this));
uint256 newWantBal = IERC20(want).balanceOf(address(this));
require(newWantBal > wantBal, "StrategyAave: !newWantBal");
wantBal = newWantBal;
}
uint256 withdrawAmt = amount < wantBal ? amount : wantBal;
uint256 fee = chargeWithdrawalFee(withdrawAmt);
if (withdrawAmt > fee) {
TransferHelper.safeTransfer(want, vault, withdrawAmt - fee);
}
emit Withdraw(balanceOf());
}
Impact
A malicious user may purposefully withdraw small amounts of tokens, just enough that the Vault has to withdraw additional tokens from the strategy, incurring fees. This can be repeated multiple times, leading to a situation where the Vault is drained of its funds.
Recommendations
We recommend disallowing the call of earn
for all but privileged users, such as the owner of the Vault. This way, the Vault can control the interval at which the earn
function is called and can ensure there are enough funds in the Vault to cover user withdrawals.
Remediation
This issue has been acknowledged by WOOFI, and a fix was implemented in commit 651aae35↗.