Under certain circumstances, e.g. annualizedFee being minted to feeBeneficiary between the time user sent the transaction and the transaction being packed into the block and causing amounts of underlying tokens for each basketToken to decrease. It's possible or even most certainly that there will be some leftover basket underlying tokens, as BasketFacet.sol#joinPool() will only transfer required amounts of basket tokens from Join contracts.
However, in the current implementation, only the leftover inputToken is returned.
As a result, the leftover underlying tokens won't be returned to the user, which constitutes users' fund loss.
Furthermore, the leftover tokens in the SingleTokenJoinV2 contract can be stolen by calling joinTokenSingle() with fake outputBasket contract and swap.exchange contract.
Recomandation
Consider changing to:
Calling IBasketFacet.calcTokensForAmount() first and only swap for exactly the desired amounts of tokens (like SingleTokenJoin.sol);
Handle
WatchPug
Vulnerability details
Under certain circumstances, e.g.
annualizedFee
being minted tofeeBeneficiary
between the time user sent the transaction and the transaction being packed into the block and causing amounts of underlying tokens for each basketToken to decrease. It's possible or even most certainly that there will be some leftover basket underlying tokens, asBasketFacet.sol#joinPool()
will only transfer required amounts of basket tokens from Join contracts.However, in the current implementation, only the leftover inputToken is returned.
As a result, the leftover underlying tokens won't be returned to the user, which constitutes users' fund loss.
https://github.com/code-423n4/2021-12-amun/blob/cf890dedf2e43ec787e8e5df65726316fda134a1/contracts/basket/contracts/singleJoinExit/SingleTokenJoinV2.sol#L57-L78
https://github.com/code-423n4/2021-12-amun/blob/cf890dedf2e43ec787e8e5df65726316fda134a1/contracts/basket/contracts/facets/Basket/BasketFacet.sol#L143-L168
Furthermore, the leftover tokens in the
SingleTokenJoinV2
contract can be stolen by callingjoinTokenSingle()
with fakeoutputBasket
contract andswap.exchange
contract.Recomandation
Consider changing to:
IBasketFacet.calcTokensForAmount()
first and only swap for exactly the desired amounts of tokens (likeSingleTokenJoin.sol
);