Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lenders are unable to withdraw complete collateral #627

Closed
c4-bot-9 opened this issue Jun 14, 2024 · 4 comments
Closed

Lenders are unable to withdraw complete collateral #627

c4-bot-9 opened this issue Jun 14, 2024 · 4 comments
Labels
2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value bug Something isn't working edited-by-warden insufficient quality report This report is not of sufficient quality 🤖_primary AI based primary recommendation 🤖_89_group AI based duplicate group recommendation

Comments

@c4-bot-9
Copy link
Contributor

c4-bot-9 commented Jun 14, 2024

Lines of code

https://github.com/code-423n4/2024-05-predy/blob/main/src/PredyPool.sol#L237

Vulnerability details

Summary

The PredyPool:withdraw() function is used to withdraw either the quoteToken or the baseToken from the lending pool. However, due to a rounding issue, lenders are unable to completely exit the pool

Impact

Lenders are unable to exit completely from the PredyPool

Proof of Concept

https://github.com/code-423n4/2024-05-predy/blob/main/src/PredyPool.sol#L222 Lender supply the tokens via supply method and get equivalent bond shares which represent their actual balance in the predypool.

  1. Alice is a lender and supplies amount tokens to the PredyPool

  2. Alice will receive (amount * 1e18) / (tokenState.assetScaler) bond tokens in return, referred to as aliceTotalBondShares

  3. The tokenState.assetScaler increases after every deposit, withdrawal, and trade. Hence, at any time, the maximum amount of assets a lender can withdraw can be viewed by getAvailableCollateralValue()

    function getAvailableCollateralValue(AssetStatus memory tokenState) internal pure returns (uint256) {
        return getTotalCollateralValue(tokenState) - getTotalDebtValue(tokenState);
    }
    function getTotalCollateralValue(AssetStatus memory tokenState) internal pure returns (uint256) {
        return FixedPointMathLib.mulDivDown(tokenState.totalCompoundDeposited, tokenState.assetScaler, Constants.ONE)
            + tokenState.totalNormalDeposited;
    }

    function getTotalDebtValue(AssetStatus memory tokenState) internal pure returns (uint256) {
        return tokenState.totalNormalBorrowed;
    }
  1. At any time, Alice should be able to withdraw (total Alice's bond tokens * tokenState.assetScaler) / 1e18. So, Alice calls the withdraw function to withdraw the above amount.

  2. predypool.withdraw() calls removeAsset to compute balances and Alice's total balance.

    function removeAsset(AssetStatus storage tokenState, uint256 _supplyTokenAmount, uint256 _amount)
        internal
        returns (uint256 finalBurnAmount, uint256 finalWithdrawAmount)
    {
        if (_amount == 0) {
            return (0, 0);
        }

        require(_supplyTokenAmount > 0, "S3");

        uint256 burnAmount = FixedPointMathLib.mulDivDown(_amount, Constants.ONE, tokenState.assetScaler);

        if (_supplyTokenAmount < burnAmount) {
            finalBurnAmount = _supplyTokenAmount;
        } else {
            finalBurnAmount = burnAmount;
        }

        finalWithdrawAmount = FixedPointMathLib.mulDivDown(finalBurnAmount, tokenState.assetScaler, Constants.ONE);

        require(getAvailableCollateralValue(tokenState) >= finalWithdrawAmount, "S0");

        tokenState.totalCompoundDeposited -= finalBurnAmount;
    }

As we can see, it calculates burnAmount from (amount * 1e18) / assetScaler, which will always be less than aliceTotalBondShares (from point 2). So Alice won't be able to withdraw her complete amount ever.

Hence, the redeemed bond tokens will always be less than the maximum amount, leaving a small amount of tokens in the PredyPool for every lender

Tools Used

Manual

Recommended Mitigation Steps

Lenders should be able to exit completely i.e make sure the lender can fully redeem their bond tokens and exit the pool.

Assessed type

Math

@c4-bot-9 c4-bot-9 added 2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value bug Something isn't working labels Jun 14, 2024
c4-bot-3 added a commit that referenced this issue Jun 14, 2024
@c4-bot-12 c4-bot-12 added 🤖_89_group AI based duplicate group recommendation 🤖_primary AI based primary recommendation labels Jun 14, 2024
@howlbot-integration howlbot-integration bot added the insufficient quality report This report is not of sufficient quality label Jun 17, 2024
@Tri-pathi
Copy link

submission explains about the issue due to which user is unable to withdraw complete balance. All steps are pretty well explained.

Please have a second look. Thanks

@alex-ppg
Copy link

alex-ppg commented Jul 4, 2024

Hey @Tri-pathi, thanks for contributing to the PJQA process! This represents a validation repository finding and as such was not evaluated by me directly.

The _amount argument of the removeAsset function is in the collateral denomination. As such, the burn amount will be less than the original balance the user acquired when depositing, while the withdrawal will be the full amount.

@Tri-pathi
Copy link

@alex-ppg

As such, the burn amount will be less than the original balance the user acquired when depositing, while the withdrawal will be the full amount.

Yes, it will withdraw the amount completely, but the user will be unable to exit completely from Predy. A complete exit means the total bond tokens of the user should be zero in the protocol, but with the current implementation, it is not possible. Every user will have some dust bond tokens remaining.

@alex-ppg
Copy link

alex-ppg commented Jul 7, 2024

Hey @Tri-pathi, thank you for your feedback. I do not foresee any high-risk or medium-risk impact arising from having dust of the Predy tokens in one's account. As such, this cannot constitute an HM vulnerability and would be at-best a QA issue. Based on this fact, the submission will remain in the validation repository and no further feedback is expected.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value bug Something isn't working edited-by-warden insufficient quality report This report is not of sufficient quality 🤖_primary AI based primary recommendation 🤖_89_group AI based duplicate group recommendation
Projects
None yet
Development

No branches or pull requests

5 participants