low

Anyone with TST tokens can monitor the mempool and frontrun mint/burn functio...

Contest
Reward

Total

37.23 USDC

1.92 USDC
1.92 USDC
1.92 USDC
1.92 USDC
1.92 USDC
Selected
2.69 USDC
1.92 USDC
1.92 USDC
1.92 USDC
1.92 USDC
1.92 USDC
1.92 USDC
1.92 USDC
1.92 USDC
1.92 USDC
1.92 USDC
1.92 USDC
1.92 USDC
1.92 USDC
Selected Submission

Anyone with TST tokens can monitor the mempool and frontrun mint/burn functions to get EUROs rewards without even staking.

Severity

Medium Risk

Relevant GitHub Links

https://github.com/Cyfrin/2023-12-the-standard/blob/91132936cb09ef9bf82f38ab1106346e2ad60f91/contracts/LiquidationPool.sol#L190C12-L192C14

https://github.com/Cyfrin/2023-12-the-standard/blob/91132936cb09ef9bf82f38ab1106346e2ad60f91/contracts/LiquidationPool.sol#L183C9-L183C42

Summary

To incentivize TST stakers in the LiquidationPool.sol contract there is a function distributeFees in the LiquidationPoolManager which distribute EUROs to the stakers. If a user tries to frontrun this by quickly staking TST tokens and steal rewards this will not work since the only way to stake TST is by calling LiquidationPool::increasePosition which protects from frontrunnning by calling ILiquidationPoolManager(manager).distributeFees(); which will transfer all rewards before he could stake his TST tokens.

Vulnerability Details

However an attacker can still exploit the rewards by frontrunning not the LiquidationPoolManager::distributeFees but the SmartVaultV3's mint or burn functions.

Lets imagine a scenario where:

  1. An individual with high networth uses the Standard to borrow 10000000e18(10 million EUROs) by calling his SmartVault's mint function. If we look at the mint function we see uint256 fee = _amount * ISmartVaultManagerV3(manager).mintFeeRate() / ISmartVaultManagerV3(manager).HUNDRED_PC(); which is actually sent to the LiquidationPoolManager which is the distributed EUROs to the stakers. EUROs.mint(ISmartVaultManagerV3(manager).protocol(), fee); protocol here is the LiquidationPoolManager contract. See: Contract requires the protocol address to be a payable address. This address will be set to the LiquidationPoolManager, which has a receive function from audit details.

So the fees sent will be fee = 10000000 * 500 / 1e5 = 50,000 EUROs which is worth the attack though he can still do it with smaller amounts if he wants to. Or could even be greater amounts.

  1. before this fees could be sent to the LiquidationPoolManager the attacker quickly frontruns the transaction by calling the LiquidationPool::increasePosition and stake a large amount of TST tokens which will be added to the pendingStakes.

  2. now since he called LiquidationPool::increasePosition the current rewards will be distributed which still doesn't include the 50,000 EUROs from the whale. After his transaction the mint transaction gets executed and there is currently new 50,000 EUROs in rewards from mint fees.

  3. Attacker calls the LiquidationPoolManager::distributeFees after diluting the rewards of honest stakers and gets his share of EUROs rewards and unstake his tokens. An attacker may have to wait one day to unstake his TST tokens but this is not a problem for him because he gets back all his TST tokens + the EUROs rewards stolen.

  4. He can sell his EUROs for and buy more TST tokens and repeat the attack with larger amount of TST tokens.

This is possible because we reward even the pending stakes and assume that rewards could not be mainpulated by frontrunning because we call ILiquidationPoolManager(manager).distributeFees(); everytime a stake is increased.

Impact

Loss of EUROs rewards for innocent stakers.

Tools Used

Recommendations

Instead of rewarding both the currently staked and the pendingStakes only reward the currently staked users so that we require atleast one day for stakers to recieve rewards and cannot be manipulated by frontrunning.