Platform: Code4rena
Start Date: 27/04/2023
Pot Size: $90,500 USDC
Total HM: 4
Participants: 43
Period: 7 days
Judge: GalloDaSballo
Id: 233
League: ETH
Rank: 11/43
Findings: 2
Award: $1,533.95
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: juancito
Also found by: MiloTruck, Ruhum, SpicyMeatball, bin2chen, evmboi32, pontifex, sashik_eth, volodya, yjrwkk
1443.9307 USDC - $1,443.93
Temporary blocking withdrawals because of slashQueuedWithdrawal
function incorrectness
##Impact
The incorrectness of the slashQueuedWithdrawal
can block withdraw operations till queuedWithdrawal
argument will be changed to exclude strategie
s with PAUSED_WITHDRAWALS
parameter.
There is an indicesToSkip
optional argument for bypassing strategie
s, which can't be withdrawn. indicesToSkip
contents indices of such strategies
s. The loop in the L560 lets you sort strategie
s, but increment ++i
is placed in the wrong line. If the logic branch goes into skipping strategie
, the i
variable stays the same. So the next iteration will be with the same strategie
index.
for (uint256 i = 0; i < strategiesLength;) { // check if the index i matches one of the indices specified in the `indicesToSkip` array if (indicesToSkipIndex < indicesToSkip.length && indicesToSkip[indicesToSkipIndex] == i) { unchecked { ++indicesToSkipIndex; } } else { if (queuedWithdrawal.strategies[i] == beaconChainETHStrategy){ //withdraw the beaconChainETH to the recipient _withdrawBeaconChainETH(queuedWithdrawal.depositor, recipient, queuedWithdrawal.shares[i]); } else { // tell the strategy to send the appropriate amount of funds to the recipient queuedWithdrawal.strategies[i].withdraw(recipient, tokens[i], queuedWithdrawal.shares[i]); } unchecked { ++i; } } }
manual review
Put the increment in the bottom of the loop.
Loop
#0 - c4-pre-sort
2023-05-09T13:09:17Z
0xSorryNotSorry marked the issue as duplicate of #205
#1 - c4-judge
2023-06-08T12:23:22Z
GalloDaSballo marked the issue as satisfactory
#2 - c4-judge
2023-06-08T12:23:30Z
GalloDaSballo changed the severity to 3 (High Risk)
🌟 Selected for report: neutiyoo
Also found by: 0xSmartContract, 0xnev, Aymen0909, QiuhaoLi, ReyAdmirado, clayj, ihtishamsudo, naman1778, niser93, pontifex, tonisives, turvy_fuzz
90.0161 USDC - $90.02
strategiesLength
caching variable in the top of the function bodyDeclaring strategiesLength
caching variable in the top of the function body lets use the variable in other statements.
There are three instances:
341: { 342: require(strategies.length == shares.length, "StrategyManager.queueWithdrawal: input length mismatch"); ... 358: for (uint256 i = 0; i < strategies.length;) { ... 362: require(strategies.length == 1,
494: { 495: require(tokens.length == strategies.length, "StrategyManager.slashShares: input length mismatch"); 496: uint256 strategyIndexIndex; 497: uint256 strategiesLength = strategies.length; 498: for (uint256 i = 0; i < strategiesLength;) {
541: { 542: require(tokens.length == queuedWithdrawal.strategies.length, "StrategyManager.slashQueuedWithdrawal: input length mismatch"); ... 559: uint256 strategiesLength = queuedWithdrawal.strategies.length; 560: for (uint256 i = 0; i < strategiesLength;) {
strategiesLength
caching variable instead queuedWithdrawal.strategies.length
Using strategiesLength
caching variable instead queuedWithdrawal.strategies.length
in the line #778 will save some gas.
775: uint256 strategiesLength = queuedWithdrawal.strategies.length; 776: // if the withdrawer has flagged to receive the funds as tokens, withdraw from strategies 777: if (receiveAsTokens) { 778: require(tokens.length == queuedWithdrawal.strategies.length, "StrategyManager.completeQueuedWithdrawal: input length mismatch");
stakerStrategyShares[depositor][strategy]
in _addShares
functionUsing caching variable for stakerStrategyShares[depositor][strategy]
in _addShares
function in if
in the line #63 and as an operand in the line #644 lets save about 100 gas. In the line #644 it should be look like stakerStrategyShares[depositor][strategy] = cachedValue + shares;
629: function _addShares(address depositor, IStrategy strategy, uint256 shares) internal { ... 635: if (stakerStrategyShares[depositor][strategy] == 0) { ... 644: stakerStrategyShares[depositor][strategy] += shares;
<array>.length
in loopsCaching indicesToSkip.length
instead of looking it up in every loop can save some gas.
560: for (uint256 i = 0; i < strategiesLength;) { 561: // check if the index i matches one of the indices specified in the `indicesToSkip` array 562: if (indicesToSkipIndex < indicesToSkip.length && indicesToSkip[indicesToSkipIndex] == i) {
require(a <= b); x = b - a => require(a <= b); unchecked { x = b - a }
824: if (amount > amountToDecrement) { 825: beaconChainETHSharesToDecrementOnWithdrawal[staker] = 0; 826: // decrease `amount` appropriately, so less is sent at the end 827: amount -= amountToDecrement;
withdrawalDelayBlocks
state variable in the loopUsing the caching variable for withdrawalDelayBlocks
state variable in the loop lets save gas.
142: while (i < maxNumberOfDelayedWithdrawalsToClaim && (delayedWithdrawalsCompletedBefore + i) < _userWithdrawalsLength) { ... 146: if (block.number < delayedWithdrawal.blockCreated + withdrawalDelayBlocks) {
#0 - GalloDaSballo
2023-06-01T16:48:44Z
GAS-3 Use caching variable for stakerStrategyShares[depositor][strategy] in _addShares function 1L
Rest is OOS
#1 - c4-judge
2023-06-01T16:59:02Z
GalloDaSballo marked the issue as grade-b