Dopex - Jorgect'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: 161/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

Vulnerability details

The withdraw function is missing subtract the amount of the delegatePosition in totalWethDelegated, this can allow a user or a attacker block all the weth reserve.

Impact

A user can block all the weth reserve in the RpdxV2Core.sol, letting the contract unusuable lossing the weth reserve for ever.

Proof of Concept

The totalWethDelegated is a variable that track the total amount of weth that the protocol has available to use in bondWithDelegate function.

let´s check the addDelegate function:

file:contracts/core/RdpxV2Core.sol function addToDelegate( uint256 _amount, uint256 _fee ) external returns (uint256) { ... Delegate memory delegatePosition = Delegate({ amount: _amount, fee: _fee, owner: msg.sender, activeCollateral: 0 }); delegates.push(delegatePosition); // add amount to total weth delegated totalWethDelegated += _amount; ... }

https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/core/RdpxV2Core.sol#L941C3-L969C1

the addToDelegate function is increasing the totalWethDelegated. Now when a user bond With Delegate the amount of weth delegated are substracted in totalWethDelegated variable:

file:contracts/core/RdpxV2Core.sol function _bondWithDelegate( uint256 _amount, uint256 rdpxBondId, uint256 delegateId ) internal returns (BondWithDelegateReturnValue memory returnValues) { ... totalWethDelegated -= wethRequired; ... }

https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/core/RdpxV2Core.sol#L699C1-L744C4

if the delegatePosition still has some weth that are not been using, the owner of the delegatePosition can withdraw them

file:contracts/core/RdpxV2Core.sol function withdraw( uint256 delegateId ) external returns (uint256 amountWithdrawn) { _whenNotPaused(); _validate(delegateId < delegates.length, 14); Delegate storage delegate = delegates[delegateId]; _validate(delegate.owner == msg.sender, 9); amountWithdrawn = delegate.amount - delegate.activeCollateral; _validate(amountWithdrawn > 0, 15); delegate.amount = delegate.activeCollateral; IERC20WithBurn(weth).safeTransfer(msg.sender, amountWithdrawn); emit LogDelegateWithdraw(delegateId, amountWithdrawn); }

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

We can see perfectly that this amount of weth are no substracted in the totalWethDelegated variable.

This is problematic due the contract are performing important calculation with this variable

see the above proof of concept (this test have to be run it in Unit.t.sol in rdpxV2-core test folder):

function testIncorrectWhitdrawT() public { //Delegate son WETH uint256 delegateId = rdpxV2Core.addToDelegate(50 * 1e18, 10 * 1e8); //First check of the totalWethDelegate uint256 first_totalwethdelegate = rdpxV2Core.totalWethDelegated(); console.log("first check of the totalWethDelegate:", first_totalwethdelegate); rdpxV2Core.withdraw(delegateId); uint256 second_totalwethdelegate = rdpxV2Core.totalWethDelegated(); console.log("second check of the totalWethDelegate:", second_totalwethdelegate); assert(second_totalwethdelegate == 0); }

This assertion have to be passed but it doest´n and if we check the logs

Logs: first check of the totalWethDelegate: 50000000000000000000 second check of the totalWethDelegate: 50000000000000000000

The totalWethDelegate is never reduced.

At this point this is problematic but it could be more problematic if a user call the sync function:

file:contracts/core/RdpxV2Core.sol function sync() external { for (uint256 i = 1; i < reserveAsset.length; i++) { uint256 balance = IERC20WithBurn(reserveAsset[i].tokenAddress).balanceOf( address(this) ); if (weth == reserveAsset[i].tokenAddress) { balance = balance - totalWethDelegated; } reserveAsset[i].tokenBalance = balance; } emit LogSync(); }

https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/core/RdpxV2Core.sol#L995C3-L1008C4

This function is syncronizing the weth balance of the contract minus the totalWethDelegated but remenber that this totalWethDelegated is actually more than it should be due the problem in the withdraw functions.

So if an attacker follow the next path can block the weth funds of the contract:

  1. Calculate the amount of the resever of the contract.
  2. take a flashloan.
  3. Create a delegatePosition with the amount of the reserve.
  4. Withdraw this position.
  5. Call sync function.
  6. Repay the flashloan.
  7. The weth resever is done

Tools Used

Manual, foundry

Substract the totalWethDelegated in when a user withdraw a delegatePosition.

function withdraw( uint256 delegateId ) external returns (uint256 amountWithdrawn) { _whenNotPaused(); _validate(delegateId < delegates.length, 14); Delegate storage delegate = delegates[delegateId]; _validate(delegate.owner == msg.sender, 9); amountWithdrawn = delegate.amount - delegate.activeCollateral; _validate(amountWithdrawn > 0, 15); delegate.amount = delegate.activeCollateral; totalWethDelegated -= delegate.amount; IERC20WithBurn(weth).safeTransfer(msg.sender, amountWithdrawn); emit LogDelegateWithdraw(delegateId, amountWithdrawn); }

Assessed type

Error

#0 - c4-pre-sort

2023-09-07T08:11:31Z

bytes032 marked the issue as duplicate of #2186

#1 - c4-judge

2023-10-20T17:55:32Z

GalloDaSballo changed the severity to 2 (Med Risk)

#2 - c4-judge

2023-10-20T18:01:02Z

GalloDaSballo marked the issue as satisfactory

#3 - c4-judge

2023-10-21T07:38:55Z

GalloDaSballo changed the severity to 3 (High Risk)

#4 - c4-judge

2023-10-21T07:48:31Z

GalloDaSballo marked the issue as partial-25

#5 - jorgect207

2023-10-22T03:50:47Z

hey alex thanks for check my submission, you marked my submission as partial-25 when i check the highs qualitys reports for this submission #1972, #1788 , i saw it that i´m describing the whole issue, even i have a POC included. i think that it could be a better tag instead of partial-25 thank you so much

#6 - GalloDaSballo

2023-10-23T07:23:45Z

Agree

#7 - c4-judge

2023-10-23T07:23:45Z

GalloDaSballo marked the issue as partial-50

#8 - jorgect207

2023-10-23T15:54:45Z

thank you so much

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