code-423n4 / 2024-03-pooltogether-findings

5 stars 4 forks source link

PrizeVault is not fully complient with EIP4626 #338

Closed c4-bot-1 closed 8 months ago

c4-bot-1 commented 8 months ago

Lines of code

https://github.com/code-423n4/2024-03-pooltogether/blob/main/pt-v5-vault/src/PrizeVault.sol#L350 https://github.com/code-423n4/2024-03-pooltogether/blob/main/pt-v5-vault/src/PrizeVault.sol#L364 https://github.com/code-423n4/2024-03-pooltogether/blob/main/pt-v5-vault/src/PrizeVault.sol#L382

Vulnerability details

Impact

PrizeVault doesn't strictly follow the rules for an EIP4626 contract. The following functions are non-compliant:

Proof of Concept

EIP4626 states the following:

convertToAssets MUST NOT revert unless due to integer overflow caused by an unreasonably large input.

However we can see that in PrizeVault::convertToAssets will revert if totalDebt_ is 0 (due to division by zero error). This can happen for example if all the users withdrew their balances and claimed rewards and yieldFeeBalance > _asset.balanceOf(address(this)).

maxDeposit MUST NOT rely on balanceOf of asset

And we can see the maxDeposit function in PrizeVault:

function maxDeposit(address) public view returns (uint256) {
    uint256 _totalSupply = totalSupply();
    uint256 totalDebt_ = _totalDebt(_totalSupply);
    if (totalAssets() < totalDebt_) return 0;

    // the vault will never mint more than 1 share per asset, so no need to convert supply limit to assets @audit is it true??
    uint256 twabSupplyLimit_ = _twabSupplyLimit(_totalSupply);
    uint256 _maxDeposit;
    uint256 _latentBalance = _asset.balanceOf(address(this));
    uint256 _maxYieldVaultDeposit = yieldVault.maxDeposit(address(this));
    if (_latentBalance >= _maxYieldVaultDeposit) {
        return 0;
    } else {
        unchecked {
            _maxDeposit = _maxYieldVaultDeposit - _latentBalance;
        }
        return twabSupplyLimit_ < _maxDeposit ? twabSupplyLimit_ : _maxDeposit;
    }
}

And indeed it depends on _latentBalance which is the balanceOf.

maxDeposit MUST return the maximum amount of assets deposit would allow to be deposited for receiver and not cause a revert

There is no check in PrizeVault::deposit or PrizeVault::mint to check that users don't deposit more than maxDeposit which opposes the EIP.

The same goes for maxMint.

Tools Used

Manual Review

Recommended Mitigation Steps

Review https://eips.ethereum.org/EIPS/eip-4626 and follow the stated rules.

Assessed type

ERC4626

raymondfam commented 8 months ago

totalDebt_ is 0 leading to division by zero doesn't make sense given that assets would have been 0 too and 0 is being returned here:

https://github.com/code-423n4/2024-03-pooltogether/blob/main/pt-v5-vault/src/PrizeVault.sol#L358-L359

c4-pre-sort commented 8 months ago

raymondfam marked the issue as insufficient quality report

c4-pre-sort commented 8 months ago

raymondfam marked the issue as duplicate of #71

c4-judge commented 8 months ago

hansfriese marked the issue as unsatisfactory: Insufficient proof