Dopex - mussucal's results

A rebate system for option writers in the Dopex Protocol.

General Information

Platform: Code4rena

Start Date: 21/08/2023

Pot Size: $125,000 USDC

Total HM: 26

Participants: 189

Period: 16 days

Judge: GalloDaSballo

Total Solo HM: 3

Id: 278

League: ETH

Dopex

Findings Distribution

Researcher Performance

Rank: 160/189

Findings: 1

Award: $0.07

🌟 Selected for report: 0

🚀 Solo Findings: 0

Lines of code

https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/core/RdpxV2Core.sol#L975-L990 https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/core/RdpxV2Core.sol#L995-L1008

Vulnerability details

Impact

Calling 'rdpxV2Core.bondWithDelegate()' with extra WETH and then calling 'rdpxV2Core.withdraw()' to withdraw the unused WETH. After which 'rdpxV2Core.bond()' or 'rdpxV2Core.bondWithDelegate()' is called. The WETH backing reserve value is not correct.

Proof of Concept

In 'rdpxV2Core.withdraw()', the public variable totalWethDelegated is not reduced. Calling 'rdpxV2Core.sync()' after 'rdpxV2Core.withdraw()' causes arithmetic overflow in line 1002.

A modified test case from tests/rdpxV2-core/Unit.t.sol/testBondWithDelegate()


function testBondWithDelegateWithExtraWeth() public {
    // assert token reserves
    (, uint256 rdpxBalance, ) = rdpxV2Core.getReserveTokenInfo("RDPX");
    (, uint256 wethBalance, ) = rdpxV2Core.getReserveTokenInfo("WETH");

    /// add to delegate with different fees
    uint256 delegateId = rdpxV2Core.addToDelegate(50 * 1e18, 10 * 1e8);

    // test bond with delegate
    uint256[] memory _amounts = new uint256[](1);
    uint256[] memory _delegateIds = new uint256[](1);
    _delegateIds[0] = 0;
    _amounts[0] = 1 * 1e18;

    // address 1 bonds
    /// check user balance
    uint256 userRdpxBalance = rdpx.balanceOf(address(1));
    uint256 userWethBalance = weth.balanceOf(address(1));

    (uint256 rdpxRequired, uint256 wethRequired) = rdpxV2Core.calculateBondCost(
      1 * 1e18,
      0
    );

    vm.prank(address(1));
    rdpx.approve(address(rdpxV2Core), 2 * 1e18);
    vm.prank(address(1), address(1));
    (uint256 userAmount, uint256[] memory delegateAmounts) = rdpxV2Core
      .bondWithDelegate(address(1), _amounts, _delegateIds, 0);

    // check the correct amounts have been transferred
    assertEq(rdpx.balanceOf(address(1)), userRdpxBalance - rdpxRequired);
    assertEq(weth.balanceOf(address(1)), userWethBalance);
    uint256 amount;
    uint256 activeCollateral;
    (, amount, , activeCollateral) = rdpxV2Core.delegates(delegateId);

    assertEq(amount, 50 * 1e18);
    assertEq(activeCollateral, wethRequired);

    rdpxV2Core.withdraw(_delegateIds[0]);                                 //Comment these two lines for the
    rdpxV2Core.sync();                                                    //test to pass

    assertEq(
      rdpxV2ReceiptToken.balanceOf(address(rdpxV2Core)),
      userAmount + delegateAmounts[0]
    );

    // assert token reserves
    (, rdpxBalance, ) = rdpxV2Core.getReserveTokenInfo("RDPX");
    (, wethBalance, ) = rdpxV2Core.getReserveTokenInfo("WETH");

    assertEq(rdpxBalance, 1275 * 1e15);
    assertGt(wethBalance, 245 * 1e15);
  }

Tools Used

Manual review and foundry.

'totalWethDelegated' needs to be decremented by the amount of unused WETH withdrawn in 'rdpxV2Core.withdraw()'.

Assessed type

Error

#0 - c4-pre-sort

2023-09-07T07:59:10Z

bytes032 marked the issue as duplicate of #2186

#1 - c4-judge

2023-10-20T17:52:53Z

GalloDaSballo marked the issue as satisfactory

#2 - c4-judge

2023-10-20T17:55:32Z

GalloDaSballo changed the severity to 2 (Med Risk)

#3 - c4-judge

2023-10-21T07:38:54Z

GalloDaSballo changed the severity to 3 (High Risk)

#4 - c4-judge

2023-10-21T07:41:00Z

GalloDaSballo marked the issue as partial-50

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