Summary
emergencyWithdraw in FeeCollector directly sends token to treasury, which leads to loss of funds because Treasury can't handle direct transfers. Instead the deposit should be used.
Vulnerability Details
We can see that emergencyWithdraw uses safeTransfer to transfer tokens to the Treasury contract.
function emergencyWithdraw(address token) external override whenPaused {
if (!hasRole(EMERGENCY_ROLE, msg.sender)) revert UnauthorizedCaller();
if (token == address(0)) revert InvalidAddress();
uint256 balance;
if (token == address(raacToken)) {
balance = raacToken.balanceOf(address(this));
raacToken.safeTransfer(treasury, balance);
} else {
balance = IERC20(token).balanceOf(address(this));
SafeERC20.safeTransfer(IERC20(token), treasury, balance);
}
emit EmergencyWithdrawal(token, balance);
}
If we look at the deposit function in the Treasury contract we can see that it increases _balances state variable.
function deposit(address token, uint256 amount) external override nonReentrant {
if (token == address(0)) revert InvalidAddress();
if (amount == 0) revert InvalidAmount();
IERC20(token).transferFrom(msg.sender, address(this), amount);
_balances[token] += amount;
_totalValue += amount;
emit Deposited(token, amount);
}
When manager tries to withdraw tokens using withdraw function, the call will revert since the tokens were sent directly and _balances state variables was not increased.
function withdraw(
address token,
uint256 amount,
address recipient
) external override nonReentrant onlyRole(MANAGER_ROLE) {
if (token == address(0)) revert InvalidAddress();
if (recipient == address(0)) revert InvalidRecipient();
if (_balances[token] < amount) revert InsufficientBalance();
_balances[token] -= amount;
_totalValue -= amount;
IERC20(token).transfer(recipient, amount);
emit Withdrawn(token, amount, recipient);
}
Impact
Tokens that are sent directly to the Treasury are lost. FeeCollector does that using emergencyWithdraw function.
Tools Used
Manual Review, Hardhat
Recommendations
In emergencyWithdraw use deposit function to send tokens to Treasury.