Integer Division Truncation in LiquidStakingPool Leads to Loss of User Funds
stake() computes stMATAmount = (amount * 1e18) / exchangeRate(). When exchangeRate is high relative to amount, the division truncates to zero so the contract takes the user's MAT but mints no stMAT in return.
Description
The LiquidStakingPool contract contains an integer division truncation vulnerability in its staking mechanism. When users stake small amounts of MAT tokens while the exchange rate is high, the calculation of stMAT tokens can result in zero tokens due to solidity's integer division truncation. This causes users to lose their staked MAT tokens without receiving any stMAT tokens in return.
The issue occurs in the stake function when calculating how many stMAT tokens to mint:
uint256 stMATAmount = (amount * 1e18) / exchangeRate();
If exchangeRate() is significantly higher than amount, the division will result in a value less than 1, which gets truncated to 0 in integer division. The contract will then proceed to take the user's MAT tokens but mint 0 stMAT tokens to them.
As the protocol matures and accumulates rewards, the exchange rate naturally increases over time, making this vulnerability more likely to affect users with smaller stake amounts.
Impact
A test demonstrated the vulnerability:
- Established a high exchange rate (450:1) by simulating accumulated rewards
- A user attempted to stake 400 wei of MAT tokens
- The calculation
(400 * 1e18) / 450e18 = 0due to integer truncation - The user's MAT tokens were transferred, but they received 0 stMAT tokens
The user pays MAT but receives nothing in return, a direct loss of funds proportional to the stake amount.
Recommendation
Implement a minimum stake amount check in the stake function:
function stake(uint256 amount) external {if (amount == 0) revert InsufficientAmount();uint256 currentRate = exchangeRate();uint256 stMATAmount = (amount * 1e18) / currentRate;// Add this check to prevent truncation lossif (stMATAmount == 0) revert StakeTooSmall(amount, currentRate);bool success = matToken.transferFrom(msg.sender, address(this), amount);if (!success) revert TransferFailed();stakingPool.stake(amount);_mint(msg.sender, stMATAmount);emit Staked(msg.sender, amount, stMATAmount);}

