Users might lose some of the rewards they’re supposed to get.
Vulnerability Details
During the zapOut process, the zapper will perform a swap based on the input swapData, and if there’s any leftover, it’ll return it to the msg.sender as the comment says.
/// @dev Bera is sent to the receiver. Any extra token0 and token1 is sent back to the _msgSender().
/// @dev integrating contracts must handle any returned token0 and token1
function zapOutNative(
address receiver,
SingleTokenSwap calldata swapData0,
SingleTokenSwap calldata swapData1,
IZapper.KodiakVaultUnstakingParams calldata unstakeParams,
IZapper.VaultRedeemParams calldata redeemParams
) public nonReentrant onlyWhitelistedKodiakVaults(unstakeParams.kodiakVault) returns (uint256 totalNativeOut) {
(IERC20 token0, IERC20 token1, uint256 token0Debt, uint256 token1Debt) = _yeetOut(redeemParams, unstakeParams);
if (token0Debt == 0 && token1Debt == 0) {
return (0);
}
totalNativeOut = _swapToWBERA(token0, token1, token0Debt, token1Debt, swapData0, swapData1);
_sendNativeToken(receiver, totalNativeOut);
}
Those tokens should actually belong to the user since they were exchanged using their vault share.
Impact Details
Users might lose some of the rewards they’re supposed to get and it’s really hard for users to set swapData.inputAmount to match the returned amount because the price of the share keeps changing.
The user calls claimRewardsInNative and owns 100 vault shares, which at the current market price can be exchanged for 50 token0. So, the user sets swapData.inputAmount to 50.
When the user’s transaction is executed on-chain, the price changes, and 100 vault shares can now be exchanged for 60 token0.
Since the user set inputAmount to 50, they only receive the native token equivalent of 50 token0. The remaining 10 token0 are transferred to the StakeV2 contract, and the user can no longer access them, resulting in the user losing the reward equivalent to those 10 token0.