sherlock-audit / 2024-07-sense-points-marketplace-judging

2 stars 0 forks source link

KupiaSec - Users can front-run the resetting of `RedemptionParams` to generate profit #173

Closed sherlock-admin3 closed 2 weeks ago

sherlock-admin3 commented 2 weeks ago

KupiaSec

High

Users can front-run the resetting of RedemptionParams to generate profit

Summary

When the operator of the protocol resets RedemptionParams for a specific pointsId, the rewardsPerPToken is adjusted. Users can exploit this change in the exchange rate between pToken and the reward token to generate profit. They can easily exchange rewards for pToken at a lower price and then re-exchange those pTokens for rewards at a higher price.

Vulnerability Detail

Consider the following scenario:

Assume: redemptionFee = 0.1e18 (10%).

  1. For a specific pointsId:

    • rewardsPerPToken = 1e18 (indicating a 1:1 ratio of pToken to reward token).
    • isMerkleBased = false (allows free conversion of reward tokens to pTokens and free redemption without any Merkle proof).
  2. The protocol operator plans to reset the rewardsPerPToken for the pointsId to 1.2e18 (resulting in a 1.2:1 ratio of pToken to reward token).

  3. Alice front-runs this transaction by converting 100 reward tokens to pTokens.

    • Alice receives 100 pTokens (see L244) since the exchange rate is 1:1.
      
      function convertRewardsToPTokens(address _receiver, bytes32 _pointsId, uint256 _amountToConvert) public {
          ...

    244 uint256 pTokensToMint = FixedPointMathLib.divWadDown(_amountToConvert, rewardsPerPToken); // Round down for mint.

        ...

    251 pTokens[_pointsId].mint(_receiver, pTokensToMint);

        ...
  4. After the operator's transaction succeeds, the rewardsPerPToken is updated to 1.2e18. Alice then redeems her rewards using her 100 pTokens, setting the parameter amountToClaim to 120.

    • The amount of pTokens burned is 100 (see L191) since the exchange rate is now 1.2:1 (and Alice has 100 pTokens).
    • Alice can claim at least 120 * 0.9 = 108 reward tokens (accounting for the 10% redemptionFee).
        function redeemRewards(Claim calldata _claim, address _receiver) public {
            ...
    
    191     uint256 pTokensToBurn = FixedPointMathLib.divWadUp(amountToClaim, rewardsPerPToken);

As a result, Alice gains 108 - 100 = 8 reward tokens as unfair profit.

Impact

Users can gain unfair profits by front-running the reset of rewardsPerPToken.

Code Snippet

https://github.com/sherlock-audit/2024-07-sense-points-marketplace/tree/main/point-tokenization-vault/contracts/PointTokenVault.sol#L229-L254

https://github.com/sherlock-audit/2024-07-sense-points-marketplace/tree/main/point-tokenization-vault/contracts/PointTokenVault.sol#L172-L226

Tool used

Manual Review

Recommendation

It is recommended to implement a cooldown period for redeeming after converting rewards to pTokens.