Platform: Code4rena
Start Date: 30/04/2024
Pot Size: $112,500 USDC
Total HM: 22
Participants: 122
Period: 8 days
Judge: alcueca
Total Solo HM: 1
Id: 372
League: ETH
Rank: 68/122
Findings: 2
Award: $2.70
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: pauliax
Also found by: 0rpse, 0x73696d616f, 0xAadi, 0xCiphky, 0xPwned, 0xhacksmithh, 0xnev, 0xnightfall, 0xordersol, 14si2o_Flint, Aamir, Aymen0909, BiasedMerc, DanielArmstrong, Fassi_Security, FastChecker, GoatedAudits, Greed, KupiaSec, LessDupes, Maroutis, NentoR, OMEN, SBSecurity, Stefanov, TheFabled, adam-idarrha, ak1, aman, araj, aslanbek, b0g0, baz1ka, bigtone, blutorque, carlitox477, carrotsmuggler, crypticdefense, eeshenggoh, fyamf, gesha17, gjaldon, grearlake, guhu95, honey-k12, hunter_w3b, ilchovski, josephdara, kinda_very_good, lanrebayode77, m_Rassska, maxim371, mt030d, mussucal, oakcobalt, p0wd3r, peanuts, rbserver, shui, siguint, t0x1c, tapir, twcctop, ustazz, xg, zhaojohnson, zigtur, zzykxx
0.0026 USDC - $0.00
In the calculateTVLs
function of the RestakeManager.sol
contract. The function miscalculates the Total Value Locked (TVL) for the withdrawal queue by incorrectly using the index of operatorDelegators
(i
) instead of the collateralTokens
array index (j
) when fetching the token address to calculate its value.
The impact of this issue is significant as it leads to an incorrect calculation of the total value in the withdrawal queue, potentially resulting in distorted TVL metrics for the protocol. This could cause inaccurate information to be presented to users and possibly affect decision-making processes related to staking, governance, and the general economics of the protocol.
The incorrect code snippet is as follows:
if (!withdrawQueueTokenBalanceRecorded) { totalWithdrawalQueueValue += renzoOracle.lookupTokenValue( collateralTokens[i], collateralTokens[j].balanceOf(withdrawQueue) ); }
The correct code should reference the collateralTokens[j]
for both the token address and its balance held by withdrawQueue
as demonstrated below:
if (!withdrawQueueTokenBalanceRecorded) { totalWithdrawalQueueValue += renzoOracle.lookupTokenValue( collateralTokens[j], collateralTokens[j].balanceOf(withdrawQueue) ); }
The issue was identified through manual code review and cross-referencing the logical flow of the contract functions.
To mitigate this issue, the following steps are recommended:
i
to j
when obtaining the token address within the lookupTokenValue
function call.Error
#0 - c4-judge
2024-05-16T10:29:15Z
alcueca marked the issue as satisfactory
#1 - c4-judge
2024-05-16T10:38:47Z
alcueca changed the severity to 2 (Med Risk)
#2 - c4-judge
2024-05-16T10:39:08Z
alcueca changed the severity to 3 (High Risk)
#3 - c4-judge
2024-05-20T04:26:26Z
alcueca changed the severity to 2 (Med Risk)
#4 - c4-judge
2024-05-23T13:47:21Z
alcueca changed the severity to 3 (High Risk)
🌟 Selected for report: zigtur
Also found by: 0x73696d616f, 0xBeastBoy, 0xCiphky, Aymen0909, FastChecker, LessDupes, NentoR, Sathish9098, TECHFUND, TheFabled, ak1, bigtone, cu5t0mpeo, eeshenggoh, guhu95, ilchovski, josephdara, ladboy233, mt030d, oakcobalt, rbserver, t0x1c, tapir, xg
2.6973 USDC - $2.70
The WithdrawQueue
contract inherits from OpenZeppelin's PausableUpgradeable
contract but does not properly implement the pause mechanism on its functions. The pause()
and unpause()
functions are present, allowing an authorized account (in this case, the withdraw queue admin) to toggle the paused state of the contract. However, the contract's core functionality does not utilize the whenNotPaused
and whenPaused
modifiers provided by PausableUpgradeable
. This means that the contract operations such as withdrawing funds or filling the withdraw buffer may still be executed even when the contract is expected to be paused, rendering the pause feature ineffective for its intended emergency stop mechanism.
The main impact of this issue is that the admin cannot effectively pause the contract's sensitive operations in the event of an emergency, such as a detected vulnerability, ongoing attack, or other critical situations that may require halting operations to prevent potential loss of funds or further damage. This vulnerability exposes the contract to increased risks, as the emergency stop functionality cannot be relied upon when needed.
Within the WithdrawQueue
contract, the pause
and unpause
functions simply toggle the state variable _paused
through internal calls to _pause()
and _unpause()
without enforcing the actual paused state in any of the contract's critical functions.
However, we do not see the use of modifiers whenNotPaused
or whenPaused
in the provided code snippet of the WithdrawQueue
contract. This suggests that the critical functions of the contract are not protected from being called while the contract is in a paused state.
Expected implementation:
function withdraw(uint256 _amount, address _assetOut) external nonReentrant whenNotPaused { // ...function logic } // Any other critical functions should have similar modifier checks
Since none of the critical functions use these modifiers, the contract can continue to operate as if it is not paused, which is a significant lapse in the security considerations for emergency situations.
Manual code review was the primary method used to identify this lack of pause functionality enforcement.
To address this issue, it is recommended that:
The whenNotPaused
modifier should be applied to all of the contract's functions that should not be executable while the contract is paused. This typically includes all state-changing functions that can move funds or alter critical data.
The whenPaused
modifier should be used in functions designed to only function when the contract is paused if such functions exist.
Access Control
#0 - c4-judge
2024-05-16T10:48:13Z
alcueca changed the severity to 2 (Med Risk)
#1 - c4-judge
2024-05-24T10:13:31Z
alcueca marked the issue as satisfactory