Open code423n4 opened 2 years ago
Actually I don't see how this could lead to fund loss. I think this one is a bug. @0xleastwood what do you think?
my understanding is that users won't be able to withdraw pushed funds @MihanixA
so fund loss is related to not being able to withdraw rather than by extracting value from the protocol
While we agree that this will prevent full withdrawal of the funds, that wil be limited to only a couple of wei's which is the yearn precision loss. So in case you put 100eth you will be able to recover 100eth - 1wei. So we'd rather name the issue "some small amounts cannot be withdrawn from the pool"
If my understanding is correct, YearnVault._pull
will withdraw yTokenAmount
representing the yToken's shares and then withdraw on this amount but return tokenAmounts
where the amount withdrawn is typically less than the amount intended to be withdrawn. LpIssuer.withdraw()
will expect actualTokenAmounts
to be available to be transferred which isn't exactly in the contract's balance.
https://github.com/code-423n4/2021-12-mellow/blob/6679e2dd118b33481ee81ad013ece4ea723327b5/mellow-vaults/contracts/YearnVault.sol#L90 https://github.com/code-423n4/2021-12-mellow/blob/6679e2dd118b33481ee81ad013ece4ea723327b5/mellow-vaults/contracts/LpIssuer.sol#L152
Let's use an example:
LpIssuer.withdraw()
with tokensAmount[0]
equal to 100 tokens. Let's ignore the lpTokenAmount
argument for the sake of this example._subvault().pull
is called on this tokensAmount[0]
.yTokenAmount
is calculated according to ((tokenAmounts[i] * (10**yToken.decimals())) / yToken.pricePerShare());
which potentially leads to a slightly truncated output.yToken.withdraw()
.yToken.withdraw()
is likely less than 100 tokens and is sent to the LpIssuer.sol
contract but actualTokenAmounts[0]
is equal to 100 tokens.LpIssuer.withdraw()
attempts to send tokens to the withdrawer but is unable as the contract does not have sufficient balance. IERC20(_vaultTokens[i]).safeTransfer(to, actualTokenAmounts[i]);
Agreed, thank you!
Handle
WatchPug
Vulnerability details
https://github.com/code-423n4/2021-12-mellow/blob/6679e2dd118b33481ee81ad013ece4ea723327b5/mellow-vaults/test_brownie/contracts/YearnVault.sol#L84-L101
The actual token withdrew from
yToken.withdraw()
will most certainly be less than thetokenAmounts[i]
, due to precision loss in the calculation ofyTokenAmount
.As a result,
IERC20(_vaultTokens[i]).safeTransfer(to, actualTokenAmounts[i]);
inLpIssuer.sol#withdraw()
will revert due to insufficant balance.Recommendation
Change to: