PoolTogether - Fitro's results

General Information

Platform: Code4rena

Start Date: 04/03/2024

Pot Size: $36,500 USDC

Total HM: 9

Participants: 80

Period: 7 days

Judge: hansfriese

Total Solo HM: 2

Id: 332

League: ETH

PoolTogether

Findings Distribution

Researcher Performance

Rank: 75/80

Findings: 1

Award: $1.47

🌟 Selected for report: 0

🚀 Solo Findings: 0

Awards

1.4652 USDC - $1.47

Labels

bug
3 (High Risk)
satisfactory
sufficient quality report
upgraded by judge
:robot:_10_group
duplicate-59

External Links

Lines of code

https://github.com/code-423n4/2024-03-pooltogether/blob/480d58b9e8611c13587f28811864aea138a0021a/pt-v5-vault/src/PrizeVault.sol#L611-L622

Vulnerability details

Impact

claimYieldFeeShares() is used for the collection of yield fees to a specific recipient.

function claimYieldFeeShares(uint256 _shares) external onlyYieldFeeRecipient {
        if (_shares == 0) revert MintZeroShares();

        uint256 _yieldFeeBalance = yieldFeeBalance;
        if (_shares > _yieldFeeBalance) revert SharesExceedsYieldFeeBalance(_shares, _yieldFeeBalance);

@>       yieldFeeBalance -= _yieldFeeBalance;
        
        _mint(msg.sender, _shares);

        emit ClaimYieldFeeShares(msg.sender, _shares);
    }

As observed, _shares is used to specify the amount of fees to collect, the issue arises when _shares != yieldFeeBalance. The issue lies in the fact that only _shares are minted, but the entire yieldFeeBalance is subtracted. Consequently, the remaining fees are lost, specifically yieldFeeBalance - _shares.

Proof of Concept

To execute the POC copy the provided code into the PrizeVault.t.sol file.

function testYieldFeeLost() public {
        vault.setYieldFeePercentage(1e8); // 10%
        vault.setYieldFeeRecipient(bob);
        assertEq(vault.totalDebt(), 0);

        // make an initial deposit
        underlyingAsset.mint(alice, 1e18);
        vm.startPrank(alice);
        underlyingAsset.approve(address(vault), 1e18);
        vault.deposit(1e18, alice);
        vm.stopPrank();

        assertEq(vault.totalAssets(), 1e18);
        assertEq(vault.totalSupply(), 1e18);
        assertEq(vault.totalDebt(), 1e18);

        // mint yield to the vault and liquidate
        underlyingAsset.mint(address(vault), 1e18);
        vault.setLiquidationPair(address(this));
        uint256 maxLiquidation = vault.liquidatableBalanceOf(address(underlyingAsset));
        uint256 amountOut = maxLiquidation / 2;
        uint256 yieldFee = (1e18 - vault.yieldBuffer()) / (2 * 10); // 10% yield fee + 90% amountOut = 100%
        vault.transferTokensOut(address(0), bob, address(underlyingAsset), amountOut);

        assertEq(vault.totalAssets(), 1e18 + 1e18 - amountOut); // existing balance + yield - amountOut
        assertEq(vault.totalSupply(), 1e18); // no change in supply since liquidation was for assets
        assertEq(vault.totalDebt(), 1e18 + yieldFee); // debt increased since we reserved shares for the yield fee

        vm.startPrank(bob);
        vault.claimYieldFeeShares(yieldFee / 2); //claim yieldFee / 2
        assertEq(vault.totalDebt(), vault.totalSupply());
        assertEq(vault.yieldFeeBalance(), 0); //the other yieldFee / 2 is lost
        vm.stopPrank();
    }

As observed, only half of the fees are claimed, but the vault.yieldFeeBalance() is 0.

Tools Used

Manual review.

Change yieldFeeBalance -= _yieldFeeBalance for yieldFeeBalance -= _shares.

function claimYieldFeeShares(uint256 _shares) external onlyYieldFeeRecipient {
        if (_shares == 0) revert MintZeroShares();

        uint256 _yieldFeeBalance = yieldFeeBalance;
        if (_shares > _yieldFeeBalance) revert SharesExceedsYieldFeeBalance(_shares, _yieldFeeBalance);

-        yieldFeeBalance -= _yieldFeeBalance;
+        yieldFeeBalance -= _shares;   
   
        _mint(msg.sender, _shares);

        emit ClaimYieldFeeShares(msg.sender, _shares);
    }

Assessed type

Other

#0 - c4-pre-sort

2024-03-11T21:43:27Z

raymondfam marked the issue as sufficient quality report

#1 - c4-pre-sort

2024-03-11T21:43:33Z

raymondfam marked the issue as duplicate of #10

#2 - c4-pre-sort

2024-03-13T04:38:17Z

raymondfam marked the issue as duplicate of #59

#3 - c4-judge

2024-03-15T07:37:30Z

hansfriese changed the severity to 3 (High Risk)

#4 - c4-judge

2024-03-15T07:40:20Z

hansfriese marked the issue as satisfactory

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter