When LiquidationPool.sol#distributeAssets() is invoked, the protocol calculates rewards for the supported asset tokens and maps a rewards _portion to each holder depending on their positions size.
https://github.com/Cyfrin/2023-12-the-standard/blob/91132936cb09ef9bf82f38ab1106346e2ad60f91/contracts/LiquidationPool.sol#L205-L241
The rewards amount _portion is first assigned to the _position.holder in the rewards mapping, and then transferred from the manager address to the LiquidationPool. The issue here is thatPAXG is a fee-on-transfer token so later when a user tries to call LiquidationPool.sol#claimRewards() the function loops through the rewards tokens and transfers the claimable rewards to the user, but whilst the _portion amount is assigned as reward, the actual amount the contract will have received will be < _portion due to the fee-on-transfer mechanism.
https://github.com/Cyfrin/2023-12-the-standard/blob/91132936cb09ef9bf82f38ab1106346e2ad60f91/contracts/LiquidationPool.sol#L164-L180
In other words, when the for-loop gets to the PAXG token, the latter part of users that try to claim rewards will not be able to as the balance of the contract will not be enough and the for-loop will revert the whole claim function (not just for PAXG). The earlier users will be able to claim rewards since the pool will have enough to accommodate by taking away from the latter users' rewards.
As time goes on some users with stuck funds might be able to withdraw when new rewards are accrued and transferred in for other users, but there will always be people who are unable to claim their rewards since PAXG.balanceOf(address(this)) < userRewards will always be true.
Loss of rewards for a part of users when trying to claim
Manual Review
Account for fee-on-transfer tokens:
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.