The vault reward mechanism can be sandwiched by MEV to gain profits.
Vulnerability Details
When any SPL tokens are rewarded to the vault, the vault will update the VRT and total deposited amounts after calculating the reward fee:
// 1. Calculate reward fee in STpubfnprocess_update_vault_balance( program_id:&Pubkey, accounts:&[AccountInfo],) ->ProgramResult {...let st_rewards = new_st_balance.saturating_sub(vault.tokens_deposited());let st_reward_fee = vault.calculate_st_reward_fee(new_st_balance)?;// 2. Increment ST less the reward feelet st_balance_after_fees = new_st_balance.checked_sub(st_reward_fee).ok_or(VaultError::ArithmeticUnderflow)?;// @audit - update the vault's ST balance vault.set_tokens_deposited(st_balance_after_fees);// 3. Calculate the reward fee in VRTlet vrt_reward_fee = vault.calculate_vrt_mint_amount(st_reward_fee)?;// 4. Update State, with the vrt fee and the new st balance vault.set_tokens_deposited(new_st_balance);// @audit - increment the vault's VRT supply with the `vrt_reward_fee`. vault.increment_vrt_supply(vrt_reward_fee)?;...}
This instruction will update the vault's VRT and total deposited amounts, if the increased total deposited amounts greater than the increased VRT supply, the exchange rate when burning the VRT: total_deposited/VRT will be increased, such that any users can redeem more ST with the same amount of VRT.
For example, both the total deposited amounts and VRT supply are 100000, so the exchange rate is 1:1 at first, whether minting or burning VRT.
If some SPL tokens are rewarded to the vault with 10000, reward fee is 1%, the st_reward_fee = 110000 * 1% = 1100, the total deposited amounts will be updated to 110000 - 1100 = 108900, the vrt_reward_fee = 1100 * 100000/108900 = 1010, so the new VRT supply will be 100000 + 1010 = 101010. When users withdraw SPL tokens with VRT at this time, the exchange rate between SPL token and VRT supply is 108900/101010 ~= 1.078 greater than 1, so any users can gain profits by sandwich when the supported SPL tokens are rewarded to the vault.
Impact
The vault reward mechanism can be sandwiched by MEV to gain profits from other users' yield.
Implement a lock-up period for new deposited users, this should prevent any users can withdraw SPL tokens immediately after the SPL tokens are rewarded to the vault.
Or distriubute the rewards based on the time of the deposit, the longer the deposit time, the more rewards will be distributed.
Proof of Concept
Proof of Concept
This is a PoC of the above issue, please see the comments for the detailed steps, insert the case to integration_tests/tests/vault/burn_withdrawal_ticket.rs file then run cargo-build-sbf && SBF_OUT_DIR=$(pwd)/target/sbf-solana-solana/release cargo nextest run --all-features test_burn_withdrawal_ticket_basic_success_with_update_vault_balance --verbose: