code-423n4 / 2024-02-spectra-findings

4 stars 2 forks source link

Initial Deposit Rate Exploitation can Lead to Unfair Advantage and Future Deposit Dilution #286

Closed c4-bot-6 closed 8 months ago

c4-bot-6 commented 9 months ago

Lines of code

https://github.com/code-423n4/2024-02-spectra/blob/main/src/tokens/PrincipalToken.sol#L750

Vulnerability details

Impact

The PrincipalToken allows the first depositor to arbitrarily set the initial PT and IBT rates by making the first deposit. This could be exploited to dilute future depositors.

Proof of Concept

The ibtRate and ptRate state variables are initialized to some defaults. But the first depositor can arbitrarily set them by choosing the deposit amount _ibts.

File: PrincipalToken.sol
152:         ibtRate = IERC4626(ibt).previewRedeem(ibtUnit).toRay(_assetDecimals);
153:         ptRate = RayMath.RAY_UNIT;

Lines of Code

In _depositIBT() function first depositor can set arbitrary rates by deposit amount in this line:

shares = _convertIBTsToShares(_ibts - tokenizationFee, false);

Lines of Code

File: PrincipalToken.sol
750:     function _depositIBT(
751:         uint256 _ibts,
752:         address _ptReceiver,
753:         address _ytReceiver
754:     ) internal notExpired nonReentrant whenNotPaused returns (uint256 shares) {
755:         updateYield(_ytReceiver);
756:         uint256 tokenizationFee = PrincipalTokenUtil._computeTokenizationFee(
757:             _ibts,
758:             address(this),
759:             registry
760:         );
761:         _updateFees(tokenizationFee);
762:         shares = _convertIBTsToShares(_ibts - tokenizationFee, false);
763:         if (shares == 0) {
764:             revert RateError();
765:         }
766:         _mint(_ptReceiver, shares);
767:         emit Mint(msg.sender, _ptReceiver, shares);
768:         IYieldToken(yt).mint(_ytReceiver, shares);
769:     }

Lines of Code

Later depositors get diluted shares based on manipulated rates

This exploits the _convertIBTsToShares() conversion formula:

File: PrincipalToken.sol
680:     function _convertIBTsToShares(
681:         uint256 _ibts,
682:         bool _roundUp
683:     ) internal view returns (uint256 shares) {
684:         (uint256 _ptRate, uint256 _ibtRate) = _getPTandIBTRates(false);
685:         if (_ptRate == 0) {
686:             revert RateError();
687:         }
688:         shares = _ibts.mulDiv(
689:             _ibtRate,
690:             _ptRate,
691:             _roundUp ? Math.Rounding.Ceil : Math.Rounding.Floor
692:         );
693:     }

Lines of Code

Malicious user can manipulate the ratio between _ibts and _ptRate by setting the rates in their favor before others deposit.

Scenario

  1. Deploy a new PrincipalToken contract.

  2. Call _depositIBT() as the first depositor, passing a very small _ibts amount (e.g. 1 wei).

  3. This sets the initial ibtRate and ptRate to be very high (e.g. 1e18).

  4. Now the first depositor has minted a large number of PT shares (e.g. 1e18) for practically no deposited assets.

  5. When second depositor comes in, they get diluted PT shares based on high rates set by first depositor.

  6. First depositor cashes out with huge amount relative to their initial deposit.

Tools

Manual Review

Recommended Mitigation Steps

  1. Initialize a fair ptRate in the constructor based on initial APY assumptions. Don't allow it to be arbitrarily set by first depositor.

  2. Set an initial placeholder ibtRate that gets corrected after first valid deposit with minimum size. Don't let it default to zero.

  3. Pause initial deposits via Pausable and unpause after sane rates are set by owner.

  4. Limit the ratio between ibtRate and ptRate to be within a valid range.

  5. Allow owner to set initial rates after deploy before first deposit.

Assessed type

Other

c4-pre-sort commented 9 months ago

gzeon-c4 marked the issue as insufficient quality report

c4-pre-sort commented 9 months ago

gzeon-c4 marked the issue as primary issue

gzeon-c4 commented 9 months ago

they get share using the same rate, don't see where the dilution came from

c4-judge commented 8 months ago

JustDravee marked the issue as unsatisfactory: Invalid