#41624 [SC-Medium] Reward sandwich is possible in `MoneyBrinter` vault by frontrunning `compound`.
Description
Brief/Intro
Vulnerability Details
/**
* @notice Compounds rewards by swapping harvested tokens, staking in Kodiak vault and depositing into Beradrome farm
* @param swapInputTokens Array of input token addresses for swaps
* @param swapToToken0 Array of swap params to swap input tokens to token0
* @param swapToToken1 Array of swap params to swap input tokens to token1
* @param stakingParams Parameters for staking in Kodiak vault
* @param vaultStakingParams Parameters for depositing into vault
* @return uint256 Amount of island tokens minted
* @dev This function is non-reentrant and can only be called by the strategy manager
* @dev It approves tokens, performs swaps, stakes in Kodiak vault, and deposits into farm
* @dev Emits a VaultCompounded event
*/
function compound(
address[] calldata swapInputTokens,
IZapper.SingleTokenSwap[] calldata swapToToken0,
IZapper.SingleTokenSwap[] calldata swapToToken1,
IZapper.KodiakVaultStakingParams calldata stakingParams,
IZapper.VaultDepositParams calldata vaultStakingParams
) public override onlyStrategyManager nonReentrant returns (uint256) {
// By adding the staking params receiver as the vault address, we ensure that zapper returns the island Tokens to the vault
require(
stakingParams.receiver == address(this),
"Invalid staking receiver"
);
require(
swapInputTokens.length == swapToToken0.length + swapToToken1.length,
"Invalid swap data"
);
uint256 initialSupply = totalSupply();
uint256 shareValueBefore = previewRedeem(initialSupply);
_approveTokens(swapInputTokens, swapToToken0, swapToToken1);
IZapper.MultiSwapParams memory swapParams = IZapper.MultiSwapParams({
inputTokens: swapInputTokens,
swapToToken0: swapToToken0,
swapToToken1: swapToToken1
});
(uint256 islandTokensMinted, uint256 vaultSharesMinted) = zapper
.zapInWithMultipleTokens(
swapParams,
stakingParams,
vaultStakingParams
);
require(
vaultSharesMinted == 0,
"MoneyBrinter: vault shares minted while compounding"
);
require(
islandTokensMinted >= stakingParams.amountSharesMin,
"MoneyBrinter: not enough island tokens minted"
);
// deposit into farm
emit VaultCompounded(_msgSender(), islandTokensMinted);
_depositIntoFarm(islandTokensMinted);
uint256 shareValueAfter = previewRedeem(initialSupply);
require(
shareValueAfter >= shareValueBefore,
"MoneyBrinter: Bad Compound"
);
return islandTokensMinted;
}
Impact Details
References
Proof of Concept
Flow
Previous#41559 [SC-Critical] Incorrect Calculation of Accumulated Rewards Due to Unstaked TokensNext#41633 [SC-High] Users might lose some of the rewards they’re supposed to get.
Was this helpful?