Open code423n4 opened 2 years ago
Keeping it as medium severity because while protocol functionality is impacted, users can withdraw through the redeem()
function.
The new solution, thank you, Kenzo
/// @notice * EIP 4626 * function previewWithdraw(uint256 assets) public view returns (uint256 shares) { if (totalSupply == 0) { shares = 0; } else { shares = convertToShares( assets.add(assets.mul(feeBPS).div((uint(10000).sub(feeBPS)))) ); } }
Keeping it as medium severity because while protocol functionality is impacted, users can withdraw through the
redeem()
function.
Note that we use the withdraw
that relies on msg.sender as the caller in production so were not affected in practice. It's interesting that various wardens have different answers to the solution for this issue. This one seems best and I'm going with it for now!
Lines of code
https://github.com/code-423n4/2022-05-rubicon/blob/main/contracts/rubiconPools/BathToken.sol#L499
Vulnerability details
The fee is wrongly accounted for in
previewWithdraw
.Impact
Function returns wrong result; Additionally,
withdraw(assets,to,from)
will always revert. (The user can still withdraw his assets via other functions).Proof of Concept
The
previewWithdraw
function returns less shares than the required assets (notice the substraction):This won't work, because if the user wants to receive amount of
assets
, he needs to burn more shares than that to account for the fee. Not less. This will also makewithdraw(assets,to,from)
revert, because it takes the amount of shares frompreviewWithdraw
, and then checks how much assets were really sent to the user, and verifies that it's at least how much he asked for:But since the expectedShares is smaller than the original amount, and since
_withdraw
deducts the fee from expectedShares, then alwaysassets > assetsReceived
, and the function will revert.Recommended Mitigation Steps
The amount of shares that
previewWithdraw
should return is:convertToShares(assets.add(assets.mul(feeBPS).div((10000.sub(feeBPS))))
I prove this mathematically in this image.