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
Rank: 161/189
Findings: 1
Award: $0.07
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: 0xrafaelnicolau
Also found by: 0x111, 0xCiphky, 0xMosh, 0xWaitress, 0xc0ffEE, 0xkazim, 0xnev, 0xvj, ABAIKUNANBAEV, Aymen0909, Baki, ElCid, HChang26, HHK, Inspex, Jorgect, Kow, Krace, KrisApostolov, LFGSecurity, MiniGlome, Nyx, QiuhaoLi, RED-LOTUS-REACH, Talfao, Toshii, Vagner, Viktor_Cortess, Yanchuan, _eperezok, asui, atrixs6, bart1e, bin2chen, carrotsmuggler, chaduke, chainsnake, deadrxsezzz, degensec, dethera, dimulski, dirk_y, ether_sky, gizzy, glcanvas, grearlake, gumgumzum, halden, hals, kodyvim, koo, ladboy233, lanrebayode77, max10afternoon, minhtrng, mussucal, nobody2018, peakbolt, pontifex, qbs, ravikiranweb3, rvierdiiev, said, tapir, ubermensch, volodya, wintermute, yashar, zaevlad, zzebra83
0.0734 USDC - $0.07
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.
A user can block all the weth reserve in the RpdxV2Core.sol, letting the contract unusuable lossing the weth reserve for ever.
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; ... }
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; ... }
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); }
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(); }
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:
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); }
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