Stader Labs - niser93's results

Decentralized ETH liquid staking protocol with 4 ETH bond for anyone to be a node operator.

General Information

Platform: Code4rena

Start Date: 02/06/2023

Pot Size: $100,000 USDC

Total HM: 15

Participants: 75

Period: 7 days

Judge: Picodes

Total Solo HM: 5

Id: 249

League: ETH

Stader Labs

Findings Distribution

Researcher Performance

Rank: 42/75

Findings: 2

Award: $40.19

QA:
grade-b
Gas:
grade-b

🌟 Selected for report: 0

🚀 Solo Findings: 0

Awards

18.5651 USDC - $18.57

Labels

bug
grade-b
QA (Quality Assurance)
Q-04

External Links

Low Risk Issues

IssueInstances
L-01Loss of precision9
L-02Setters should have initial value check10
L-03Use of floating pragma1

Not Critical Issues

IssueInstances
NC-01Use scientific notation (e.g. 1e18) rather than exponentiation (e.g. 10**18)6
NC-02Typos1
NC-03Variable names don't follow the Solidity style guide1
NC-04public functions not called by the contract should be declared external instead12
NC-05Let's return default value instead of explicity write it1

Low Risk Issues

L-01

Loss of precision

Description

Division by large numbers may result in the result being zero, due to solidity not supporting fractions. Consider requiring a minimum amount for the numerator to ensure that it is always larger than the denominator

<details> <summary>There are 9 instances of this issue not reported by bot race:</summary>
File: PermissionedNodeRegistry.sol 201 uint256 validatorPerOperator = _numValidators / totalActiveOperatorCount;

https://github.com/code-423n4/2023-06-stader/blob/main/contracts/PermissionedNodeRegistry.sol#L201

File: PermissionlessPool.sol 128 uint256 requiredValidators = msg.value / (staderConfig.getFullDepositSize() - DEPOSIT_NODE_BOND);

https://github.com/code-423n4/2023-06-stader/blob/main/contracts/PermissionlessPool.sol#L128

File: PoolSelector.sol 61 uint256 poolTotalTarget = (poolWeights[_poolId] * totalValidatorsRequired) / POOL_WEIGHTS_SUM; 93 uint256 remainingValidatorsToDeposit = ethToDeposit / poolDepositSize;

https://github.com/code-423n4/2023-06-stader/blob/main/contracts/PoolSelector.sol#L61

File: PoolUtils.sol 263 protocolShare = (protocolFeeBps * _userShareBeforeCommision) / staderConfig.getTotalFee(); 265 operatorShare = (_totalRewards * collateralETH) / TOTAL_STAKED_ETH; 266 operatorShare += (operatorFeeBps * _userShareBeforeCommision) / staderConfig.getTotalFee();

https://github.com/code-423n4/2023-06-stader/blob/main/contracts/PoolUtils.sol#L261

File: StaderOracle.sol 603 return (block.number / updateFrequency) * updateFrequency; 665 !(newExchangeRate >= (currentExchangeRate * (ER_CHANGE_MAX_BPS - erChangeLimit)) / ER_CHANGE_MAX_BPS &&

https://github.com/code-423n4/2023-06-stader/blob/main/contracts/StaderOracle.sol#L603

</details>

L-02

Setters should have initial value check

Description

Setters should have initial value check to prevent assigning wrong value to the variable. Assginment of wrong value can lead to unexpected behavior of the contract.

<details> <summary>There are 10 instances of this issue:</summary>
File: Penalty.sol @audit: unchecked values _pubkey, _amount 53 function setAdditionalPenaltyAmount(bytes calldata _pubkey, uint256 _amount) external override {

https://github.com/code-423n4/2023-06-stader/blob/main/contracts/Penalty.sol#L53

File: StaderConfig.sol @audit: unchecked values key, val 471 function setConstant(bytes32 key, uint256 val) internal { @audit: unchecked values key, val 476 function setVariable(bytes32 key, uint256 val) internal { @audit: unchecked values key, val 481 function setAccount(bytes32 key, address val) internal { @audit: unchecked values key, val 487 function setContract(bytes32 key, address val) internal { @audit: unchecked values key, val 493 function setToken(bytes32 key, address val) internal {

https://github.com/code-423n4/2023-06-stader/blob/main/contracts/StaderConfig.sol#L471

File: StaderOracle.sol @audit: unchecked values _updateFrequency 539 function setSDPriceUpdateFrequency(uint256 _updateFrequency) external override { @audit: unchecked values _updateFrequency 544 function setValidatorStatsUpdateFrequency(uint256 _updateFrequency) external override { @audit: unchecked values _updateFrequency 549 function setWithdrawnValidatorsUpdateFrequency(uint256 _updateFrequency) external override { @audit: unchecked values _updateFrequency 554 function setMissedAttestationPenaltyUpdateFrequency(uint256 _updateFrequency) external override {

https://github.com/code-423n4/2023-06-stader/blob/main/contracts/StaderOracle.sol#L539

</details>

L-03

Use of floating pragma

Description

Contracts should be deployed with the same compiler version and flags that they have been tested with thoroughly. Locking the pragma helps to ensure that contracts do not accidentally get deployed using, for example, an outdated compiler version that might introduce bugs that affect the contract system negatively.(https://swcregistry.io/docs/SWC-103)

File: VaultProxy.sol 2 pragma solidity ^0.8.16;

https://github.com/code-423n4/2023-06-stader/blob/main/contracts/VaultProxy.sol#L2

Not Critical Issues

NC-01

Use scientific notation (e.g. 1e18) rather than exponentiation (e.g. 10**18)

Description

While the compiler knows to optimize away the exponentiation, it's still better coding practice to use idioms that do not require compiler optimization, if they exist

<details> <summary>There are 6 instances of this issue not reported by bot race:</summary>
File: PoolSelector.sol 21 uint256 public constant POOL_WEIGHTS_SUM = 10000;

https://github.com/code-423n4/2023-06-stader/blob/main/contracts/PoolSelector.sol#L21

File: StaderConfig.sol 92 setConstant(TOTAL_FEE, 10000); 96 setVariable(MAX_DEPOSIT_AMOUNT, 10000 ether); 98 setVariable(MAX_WITHDRAW_AMOUNT, 10000 ether);

https://github.com/code-423n4/2023-06-stader/blob/main/contracts/StaderConfig.sol#L93

File: StaderOracle.sol 27 uint256 public constant ER_CHANGE_MAX_BPS = 10000;

https://github.com/code-423n4/2023-06-stader/blob/main/contracts/StaderOracle.sol#L27

File: UserWithdrawalManager.sol 64 maxNonRedeemedUserRequestCount = 1000;

https://github.com/code-423n4/2023-06-stader/blob/main/contracts/UserWithdrawalManager.sol#L64

</details>

NC-02

Typos

Description
File: VaultProxy.sol @audit: contrat/contract 63 /** 64 * @notice @update the owner of vault proxy contrat 65 * @dev only owner can call 66 * @param _owner new owner account 67 */

https://github.com/code-423n4/2023-06-stader/blob/main/contracts/VaultProxy.sol#L63-L67

NC-03

Variable names don't follow the Solidity style guide

Description

For constant variable names, each word should use all capital letters, with underscores separating each word (CONSTANT_CASE)

File: StaderConfig.sol 72 bytes32 public constant ETHx = keccak256('ETHx');

https://github.com/code-423n4/2023-06-stader/blob/main/contracts/StaderConfig.sol#L72

NC-04

public functions not called by the contract should be declared external instead

Description

Contracts are allowed to override their parents' functions and change the visibility from external to public.

<details> <summary>There are 12 instances of this issue not reported by bot race:</summary>
File: ETHx.sol 29 function initialize(address _admin, address _staderConfig) public initializer {

https://github.com/code-423n4/2023-06-stader/blob/main/contracts/ETHx.sol#L29

File: PermissionedNodeRegistry.sol 66 function initialize(address _admin, address _staderConfig) public initializer {

https://github.com/code-423n4/2023-06-stader/blob/main/contracts/PermissionedNodeRegistry.sol#L66

File: PermissionedPool.sol 40 function initialize(address _admin, address _staderConfig) public initializer {

https://github.com/code-423n4/2023-06-stader/blob/main/contracts/PermissionedPool.sol#L40

File: PermissionlessNodeRegistry.sol 66 function initialize(address _admin, address _staderConfig) public initializer {

https://github.com/code-423n4/2023-06-stader/blob/main/contracts/PermissionlessNodeRegistry.sol#L66

File: PermissionlessPool.sol 38 function initialize(address _admin, address _staderConfig) public initializer {

https://github.com/code-423n4/2023-06-stader/blob/main/contracts/PermissionlessPool.sol#L38

File: PoolUtils.sol 25 function initialize(address _admin, address _staderConfig) public initializer {

https://github.com/code-423n4/2023-06-stader/blob/main/contracts/PoolUtils.sol#L25

File: SDCollateral.sol 26 function initialize(address _admin, address _staderConfig) public initializer {

https://github.com/code-423n4/2023-06-stader/blob/main/contracts/SDCollateral.sol#L26

File: SocializingPool.sol 39 function initialize(address _admin, address _staderConfig) public initializer {

https://github.com/code-423n4/2023-06-stader/blob/main/contracts/SocializingPool.sol#L39

File: StaderInsuranceFund.sol 26 function initialize(address _admin, address _staderConfig) public initializer {

https://github.com/code-423n4/2023-06-stader/blob/main/contracts/StaderInsuranceFund.sol#L26

File: StaderOracle.sol 62 function initialize(address _admin, address _staderConfig) public initializer {

https://github.com/code-423n4/2023-06-stader/blob/main/contracts/StaderOracle.sol#L62

File: StaderStakePoolsManager.sol 50 function initialize(address _admin, address _staderConfig) public initializer {

https://github.com/code-423n4/2023-06-stader/blob/main/contracts/StaderStakePoolsManager.sol#L50

File: UserWithdrawalManager.sol 54 function initialize(address _admin, address _staderConfig) public initializer {

https://github.com/code-423n4/2023-06-stader/blob/main/contracts/UserWithdrawalManager.sol#L54

</details>

NC-05

Let's return default value instead of explicity write it

Description

Let empty body and consider to comment why.

File: PermissionedNodeRegistry.sol

  
  546     function getCollateralETH() external pure override returns (uint256) {
- 547       return 0;
  548     }

https://github.com/code-423n4/2023-06-stader/blob/main/contracts/PermissionedNodeRegistry.sol#L546-L548

#0 - c4-judge

2023-06-14T06:04:27Z

Picodes marked the issue as grade-b

Awards

21.6219 USDC - $21.62

Labels

bug
G (Gas Optimization)
grade-b
G-02

External Links

Gas Optimizations

IssueInstances
GO-01Multiplication/division by 2 should use bit shifting2
GO-02Avoid to define variable which are used once28
GO-03Avoid useless checks1

Optimization details

TestNameGas
AuctionTesttest_JustToIncreaseCoverage()-2096
AuctionTesttest_extractNonBidSD(uint256,uint64)2
NodeELRewardVaultTesttest_withdraw(uint128)-2739
PenaltyTesttest_JustToIncreaseCoverage()-6825
PenaltyTesttest_additionalPenaltyAmount(address,uint256,bytes)6
PenaltyTesttest_calculateMEVTheftPenalty()-38
PenaltyTesttest_updateTotalPenaltyAmount()-94
PermissionedNodeRegistryTesttest_ActivateNodeOperator(uint64,uint64)-6
PermissionedNodeRegistryTesttest_AllocateValidatorsAndUpdateOperatorId()-12
PermissionedNodeRegistryTesttest_DeactivateNodeOperator(uint64,uint64)-6
PermissionedNodeRegistryTesttest_JustToIncreaseCoverage()-200
PermissionedNodeRegistryTesttest_OnboardOperator(string,uint64,uint64)-6
PermissionedNodeRegistryTesttest_addValidatorKeys()-6
PermissionedNodeRegistryTesttest_addValidatorKeysOPCrossingMaxNonTerminalKeys()-6
PermissionedNodeRegistryTesttest_addValidatorKeysWithInsufficientSDCollateral()-6
PermissionedNodeRegistryTesttest_addValidatorKeysWithInvalidKeyCount()-6
PermissionedNodeRegistryTesttest_addValidatorKeysWithMisMatchingInputs()-6
PermissionedNodeRegistryTesttest_getAllActiveValidators(uint256,uint256)-10110
PermissionedNodeRegistryTesttest_getAllActiveValidatorsWithZeroPageNumber(uint256)-6
PermissionedNodeRegistryTesttest_getOperatorTotalNonTerminalKeys(uint64,uint64,uint256,uint256)-6
PermissionedNodeRegistryTesttest_getOperatorTotalNonTerminalKeysInvalidPagination(uint64,uint64,uint256,uint256)-6
PermissionedNodeRegistryTesttest_getValidatorsByOperator()-6
PermissionedNodeRegistryTesttest_markReadyToDepositValidator()-6
PermissionedNodeRegistryTesttest_updateNextQueuedValidatorIndex(uint64,uint256)-6
PermissionedNodeRegistryTesttest_updateOperatorDetailWithInvalidName(string,uint64,uint64,uint64)-6
PermissionedNodeRegistryTesttest_updateOperatorDetailWithZeroRewardAddr(string,uint64)-6
PermissionedNodeRegistryTesttest_updateOperatorDetails(string,uint64,uint64,uint64)-6
PermissionedNodeRegistryTesttest_withdrawnValidators()-6
PermissionlessNodeRegistryTesttest_JustToIncreaseCoverage()-111904
PermissionlessNodeRegistryTesttest_OnboardOperatorWhenPaused(string,uint64,uint64)-1
PermissionlessNodeRegistryTesttest_changeSocializingPoolStateWithSomeCoolDown(string,uint64,uint64)-2
PermissionlessNodeRegistryTesttest_getAllActiveValidators(uint256,uint256)-11875
PermissionlessNodeRegistryTesttest_getAllActiveValidatorsWithZeroPageNumber(uint256)-31
PermissionlessNodeRegistryTesttest_getNodeELVaultAddressForOptOutOperators(uint256,uint256)59
PermissionlessNodeRegistryTesttest_markReadyToDepositValidator()-124
PermissionlessNodeRegistryTesttest_withdrawnValidators()-15472
SDCollateralTesttestFail_depositSDAsCollateral_withInsufficientApproval(uint256,uint256)-7
SDCollateralTesttest_JustToIncreaseCoverage()-26458
SDCollateralTesttest_depositSDAsCollateral(uint128,uint128,uint16)-40
SDCollateralTesttest_getRemainingSDToBond(uint256,uint256)-152
SDCollateralTesttest_getRewardEligibleSD(uint256)-176
SDCollateralTesttest_sd_eth_converters(uint128)-65
SDCollateralTesttest_slashValidatorSD()-222
SDCollateralTesttest_slashValidatorSD_auctionLotNotCreated_whenNoCollateral()-49
SDCollateralTesttest_withdraw(uint128,uint128)-161
SDCollateralTesttest_withdraw_revertIfPoolThresholdNotSet(uint256)6
SDCollateralTesttest_withdraw_reverts_InsufficientSDToWithdraw(uint128,uint128)-102
----
Total-188990

GO-01

Multiplication/division by 2 should use bit shifting

Description

<x> * 2 is equivalent to <x> << 1 and <x> / 2 is the same as <x> >> 1. The MUL and DIV opcodes cost 5 gas, whereas SHL and SHR only cost 3 gas.

- n*2
+ n << 1
- n/2
+ n >> 1

There are 2 instances of this issue not reported in bot race:

File: Auction.sol

  
- 38         duration = 2 * MIN_AUCTION_DURATION;
+ 38         duration = MIN_AUCTION_DURATION<<1;

https://github.com/code-423n4/2023-06-stader/blob/main/contracts/Auction.sol#L38

File: StaderOracle.sol

  
-290         if ((submissionCount == (2 * trustedNodesCount) / 3 + 1)) {
+290         if ((submissionCount == (trustedNodesCount<<1) / 3 + 1)) {

https://github.com/code-423n4/2023-06-stader/blob/main/contracts/StaderOracle.sol#L148

Gas Report:
Using forge snapshot
|   Test Name                    | Gas     |
|--------------------------------|---------|
-| test_JustToIncreaseCoverage() | 2598490 |
+| test_JustToIncreaseCoverage() | 2596394 |
|--------------------------------|---------|
|                                |   -2096 |

The saved gas is 2096.
|   Test Name                           | Gas    |
|---------------------------------------|--------|
-| test_extractNonBidSD(uint256,uint64) | 203347 |
+| test_extractNonBidSD(uint256,uint64) | 203349 |
|---------------------------------------|--------|
|                                       |      2 |

The average lost gas is 2.
TestNameGas
AuctionTesttest_JustToIncreaseCoverage()-2096
AuctionTesttest_extractNonBidSD(uint256,uint64)2
----
Total-2094

GO-02

Avoid to define variable which are used once

Description

Avoid to declare variable if you use it once. Sometimes it could get worse in human readability, so we report only cases where it is reasonable.

There are other instances whose we don't report, because there are some contract whose aren't covered by test.

There are 3 instances of this issue:

File: NodeELRewardVault.sol

    function withdraw() external override {
        uint8 poolId = IVaultProxy(address(this)).poolId();
-       uint256 operatorId = IVaultProxy(address(this)).id();
        IStaderConfig staderConfig = IVaultProxy(address(this)).staderConfig();
        uint256 totalRewards = address(this).balance;
        if (totalRewards == 0) {
            revert NotEnoughRewardToWithdraw();
        }
        (uint256 userShare, uint256 operatorShare, uint256 protocolShare) = IPoolUtils(staderConfig.getPoolUtils())
            .calculateRewardShare(poolId, totalRewards);

        // Distribute rewards
        IStaderStakePoolManager(staderConfig.getStakePoolManager()).receiveExecutionLayerRewards{value: userShare}();
        // slither-disable-next-line arbitrary-send-eth
        UtilLib.sendValue(payable(staderConfig.getStaderTreasury()), protocolShare);
-       address operator = UtilLib.getOperatorAddressByOperatorId(poolId, operatorId, staderConfig);
        IOperatorRewardsCollector(staderConfig.getOperatorRewardsCollector()).depositFor{value: operatorShare}(
-           operator
+           UtilLib.getOperatorAddressByOperatorId(poolId, IVaultProxy(address(this)).id(), staderConfig);
        );

        emit Withdrawal(protocolShare, operatorShare, userShare);
    }

https://github.com/code-423n4/2023-06-stader/blob/main/contracts/NodeELRewardVault.sol#L24-L45

File: Penalty.sol

[L54-L55]
- bytes32 pubkeyRoot = UtilLib.getPubkeyRoot(_pubkey);
- additionalPenaltyAmount[pubkeyRoot] += _amount;
+ additionalPenaltyAmount[UtilLib.getPubkeyRoot(_pubkey)] += _amount;

[L106-L111]
- uint256 _mevTheftPenalty = calculateMEVTheftPenalty(pubkeyRoot);
- uint256 _missedAttestationPenalty = calculateMissedAttestationPenalty(pubkeyRoot);

  // Compute the total penalty for the given validator public key,
  // taking into account additional penalties and penalty reversals from the DAO.
- uint256 totalPenalty = _mevTheftPenalty + _missedAttestationPenalty + additionalPenaltyAmount[pubkeyRoot];
+ uint256 totalPenalty = calculateMEVTheftPenalty(pubkeyRoot) + calculateMissedAttestationPenalty(pubkeyRoot) + additionalPenaltyAmount[pubkeyRoot];

[L123-L129]
function calculateMEVTheftPenalty(bytes32 _pubkeyRoot) public override returns (uint256) {
    // Retrieve the epochs in which the validator violated the fee recipient change rule.
-   uint256[] memory violatedEpochs = IRatedV1(ratedOracleAddress).getViolationsForValidator(_pubkeyRoot);

    // each strike attracts `mevTheftPenaltyPerStrike` penalty
-   return violatedEpochs.length * mevTheftPenaltyPerStrike;
+   return IRatedV1(ratedOracleAddress).getViolationsForValidator(_pubkeyRoot).length * mevTheftPenaltyPerStrike;
}

https://github.com/code-423n4/2023-06-stader/blob/main/contracts/Penalty.sol#L55-L56

File: PermissionedNodeRegistry.sol

[L106-L131]
    function onboardNodeOperator(string calldata _operatorName, address payable _operatorRewardAddress)
        external
        override
        whenNotPaused
-       returns (address feeRecipientAddress)
+       returns (address)
    {
        address poolUtils = staderConfig.getPoolUtils();
        if (IPoolUtils(poolUtils).poolAddressById(POOL_ID) != staderConfig.getPermissionedPool()) {
            revert DuplicatePoolIDOrPoolNotAdded();
        }
        IPoolUtils(poolUtils).onlyValidName(_operatorName);
        UtilLib.checkNonZeroAddress(_operatorRewardAddress);
        if (nextOperatorId > maxOperatorId) {
            revert MaxOperatorLimitReached();
        }
        if (!permissionList[msg.sender]) {
            revert NotAPermissionedNodeOperator();
        }
        //checks if operator already onboarded in any pool of protocol
        if (IPoolUtils(poolUtils).isExistingOperator(msg.sender)) {
            revert OperatorAlreadyOnBoardedInProtocol();
        }
-       feeRecipientAddress = staderConfig.getPermissionedSocializingPool();
        onboardOperator(_operatorName, _operatorRewardAddress);
-       return feeRecipientAddress;
+       return staderConfig.getPermissionedSocializingPool();
    }


[]

https://github.com/code-423n4/2023-06-stader/blob/main/contracts/PermissionedNodeRegistry.sol#L106-L131

File: PermissionlessNodeRegistry.sol

    function isActiveValidator(uint256 _validatorId) internal view returns (bool) {
-       Validator memory validator = validatorRegistry[_validatorId];
-       return validator.status == ValidatorStatus.DEPOSITED;
+       return validatorRegistry[_validatorId].status == ValidatorStatus.DEPOSITED;
    }

https://github.com/code-423n4/2023-06-stader/blob/main/contracts/PermissionlessNodeRegistry.sol#L701-L704

File: SDCollateral.sol

[L43-L52]
    function depositSDAsCollateral(uint256 _sdAmount) external override {
-       address operator = msg.sender;
-       operatorSDBalance[operator] += _sdAmount;
+       operatorSDBalance[msg.sender] += _sdAmount;

-       if (!IERC20(staderConfig.getStaderToken()).transferFrom(operator, address(this), _sdAmount)) {
+       if (!IERC20(staderConfig.getStaderToken()).transferFrom(msg.sender, address(this), _sdAmount)) {
            revert SDTransferFailed();
        }

-       emit SDDeposited(operator, _sdAmount);
+       emit SDDeposited(msg.sender, _sdAmount);
    }


[L58-L73]
    function withdraw(uint256 _requestedSD) external override {
-       address operator = msg.sender;
-       uint256 opSDBalance = operatorSDBalance[operator];
+       uint256 opSDBalance = operatorSDBalance[msg.sender];

-       if (opSDBalance < getOperatorWithdrawThreshold(operator) + _requestedSD) {
+       if (opSDBalance < getOperatorWithdrawThreshold(msg.sender) + _requestedSD) {
            revert InsufficientSDToWithdraw(opSDBalance);
        }
-       operatorSDBalance[operator] -= _requestedSD;
+       operatorSDBalance[msg.sender] -= _requestedSD;

        // cannot use safeERC20 as this contract is an upgradeable contract
-       if (!IERC20(staderConfig.getStaderToken()).transfer(payable(operator), _requestedSD)) {
+       if (!IERC20(staderConfig.getStaderToken()).transfer(payable(msg.sender), _requestedSD)) {
            revert SDTransferFailed();
        }

-       emit SDWithdrawn(operator, _requestedSD);
+       emit SDWithdrawn(msg.sender, _requestedSD);
    }

[L78-L84]
    function slashValidatorSD(uint256 _validatorId, uint8 _poolId) external override nonReentrant {
        address operator = UtilLib.getOperatorForValidSender(_poolId, _validatorId, msg.sender, staderConfig);
        isPoolThresholdValid(_poolId);
        PoolThresholdInfo storage poolThreshold = poolThresholdbyPoolId[_poolId];
-       uint256 sdToSlash = convertETHToSD(poolThreshold.minThreshold);
-       slashSD(operator, sdToSlash);
+       slashSD(operator, convertETHToSD(poolThreshold.minThreshold));
    }

[L90-L99]
    function slashSD(address _operator, uint256 _sdToSlash) internal {
-       uint256 sdBalance = operatorSDBalance[_operator];
-       uint256 sdSlashed = Math.min(_sdToSlash, sdBalance);
+       uint256 sdSlashed = Math.min(_sdToSlash, operatorSDBalance[_operator]);
        if (sdSlashed == 0) {
            return;
        }
        operatorSDBalance[_operator] -= sdSlashed;
        IAuction(staderConfig.getAuctionContract()).createLot(sdSlashed);
        emit SDSlashed(_operator, staderConfig.getAuctionContract(), sdSlashed);
    }

[L166-L177]
    function getMinimumSDToBond(uint8 _poolId, uint256 _numValidator)
        public
        view
        override
        returns (uint256 _minSDToBond)
    {
        isPoolThresholdValid(_poolId);
-       PoolThresholdInfo storage poolThreshold = poolThresholdbyPoolId[_poolId];

-       _minSDToBond = convertETHToSD(poolThreshold.minThreshold);
+       _minSDToBond = convertETHToSD(poolThresholdbyPoolId[_poolId].minThreshold);
        _minSDToBond *= _numValidator;
    }

[L205-L213]
    function convertSDToETH(uint256 _sdAmount) public view override returns (uint256) {
-       uint256 sdPriceInETH = IStaderOracle(staderConfig.getStaderOracle()).getSDPriceInETH();
-       return (_sdAmount * sdPriceInETH) / staderConfig.getDecimals();
+       return (_sdAmount * IStaderOracle(staderConfig.getStaderOracle()).getSDPriceInETH()) / staderConfig.getDecimals();
    }

    function convertETHToSD(uint256 _ethAmount) public view override returns (uint256) {
-       uint256 sdPriceInETH = IStaderOracle(staderConfig.getStaderOracle()).getSDPriceInETH();
-       return (_ethAmount * staderConfig.getDecimals()) / sdPriceInETH;
+       return (_ethAmount * staderConfig.getDecimals()) / IStaderOracle(staderConfig.getStaderOracle()).getSDPriceInETH();
    }

https://github.com/code-423n4/2023-06-stader/blob/main/contracts/SDCollateral.sol#L43-L52

Gas Report:
Using forge snapshot
|   Test Name             | Gas    |
|-------------------------|--------|
-| test_withdraw(uint128) | 355541 |
+| test_withdraw(uint128) | 352802 |
|-------------------------|--------|
|                         |  -2739 |

The average saved gas is 2739.
|   Test Name                    | Gas     |
|--------------------------------|---------|
-| test_JustToIncreaseCoverage() | 2347220 |
+| test_JustToIncreaseCoverage() | 2340395 |
|--------------------------------|---------|
|                                |   -6825 |

The saved gas is 6825.
|   Test Name                                          | Gas   |
|------------------------------------------------------|-------|
-| test_additionalPenaltyAmount(address,uint256,bytes) | 61704 |
+| test_additionalPenaltyAmount(address,uint256,bytes) | 61710 |
|------------------------------------------------------|-------|
|                                                      |     6 |

The average lost gas is 6.
|   Test Name                      | Gas   |
|----------------------------------|-------|
-| test_calculateMEVTheftPenalty() | 32776 |
+| test_calculateMEVTheftPenalty() | 32738 |
|----------------------------------|-------|
|                                  |   -38 |

The saved gas is 38.
|   Test Name                      | Gas    |
|----------------------------------|--------|
-| test_updateTotalPenaltyAmount() | 227942 |
+| test_updateTotalPenaltyAmount() | 227848 |
|----------------------------------|--------|
|                                  |    -94 |

The saved gas is 94.
|   Test Name                               | Gas    |
|-------------------------------------------|--------|
-| test_ActivateNodeOperator(uint64,uint64) | 268942 |
+| test_ActivateNodeOperator(uint64,uint64) | 268936 |
|-------------------------------------------|--------|
|                                           |     -6 |

The average saved gas is 6.
|   Test Name                                   | Gas     |
|-----------------------------------------------|---------|
-| test_AllocateValidatorsAndUpdateOperatorId() | 3526864 |
+| test_AllocateValidatorsAndUpdateOperatorId() | 3526852 |
|-----------------------------------------------|---------|
|                                               |     -12 |

The saved gas is 12.
|   Test Name                                 | Gas    |
|---------------------------------------------|--------|
-| test_DeactivateNodeOperator(uint64,uint64) | 243451 |
+| test_DeactivateNodeOperator(uint64,uint64) | 243445 |
|---------------------------------------------|--------|
|                                             |     -6 |

The average saved gas is 6.
|   Test Name                    | Gas     |
|--------------------------------|---------|
-| test_JustToIncreaseCoverage() | 5303416 |
+| test_JustToIncreaseCoverage() | 5303216 |
|--------------------------------|---------|
|                                |    -200 |

The saved gas is 200.
|   Test Name                                 | Gas    |
|---------------------------------------------|--------|
-| test_OnboardOperator(string,uint64,uint64) | 400997 |
+| test_OnboardOperator(string,uint64,uint64) | 400991 |
|---------------------------------------------|--------|
|                                             |     -6 |

The average saved gas is 6.
|   Test Name              | Gas     |
|--------------------------|---------|
-| test_addValidatorKeys() | 1700563 |
+| test_addValidatorKeys() | 1700557 |
|--------------------------|---------|
|                          |      -6 |

The saved gas is 6.
|   Test Name                                          | Gas    |
|------------------------------------------------------|--------|
-| test_addValidatorKeysOPCrossingMaxNonTerminalKeys() | 246860 |
+| test_addValidatorKeysOPCrossingMaxNonTerminalKeys() | 246854 |
|------------------------------------------------------|--------|
|                                                      |     -6 |

The saved gas is 6.
|   Test Name                                          | Gas    |
|------------------------------------------------------|--------|
-| test_addValidatorKeysWithInsufficientSDCollateral() | 242052 |
+| test_addValidatorKeysWithInsufficientSDCollateral() | 242046 |
|------------------------------------------------------|--------|
|                                                      |     -6 |

The saved gas is 6.
|   Test Name                                 | Gas    |
|---------------------------------------------|--------|
-| test_addValidatorKeysWithInvalidKeyCount() | 225015 |
+| test_addValidatorKeysWithInvalidKeyCount() | 225009 |
|---------------------------------------------|--------|
|                                             |     -6 |

The saved gas is 6.
|   Test Name                                   | Gas    |
|-----------------------------------------------|--------|
-| test_addValidatorKeysWithMisMatchingInputs() | 226380 |
+| test_addValidatorKeysWithMisMatchingInputs() | 226374 |
|-----------------------------------------------|--------|
|                                               |     -6 |

The saved gas is 6.
|   Test Name                                   | Gas     |
|-----------------------------------------------|---------|
-| test_getAllActiveValidators(uint256,uint256) | 1904138 |
+| test_getAllActiveValidators(uint256,uint256) | 1894028 |
|-----------------------------------------------|---------|
|                                               |  -10110 |

The average saved gas is 10110.
|   Test Name                                             | Gas     |
|---------------------------------------------------------|---------|
-| test_getAllActiveValidatorsWithZeroPageNumber(uint256) | 1778942 |
+| test_getAllActiveValidatorsWithZeroPageNumber(uint256) | 1778936 |
|---------------------------------------------------------|---------|
|                                                         |      -6 |

The average saved gas is 6.
|   Test Name                                                          | Gas     |
|----------------------------------------------------------------------|---------|
-| test_getOperatorTotalNonTerminalKeys(uint64,uint64,uint256,uint256) | 1729560 |
+| test_getOperatorTotalNonTerminalKeys(uint64,uint64,uint256,uint256) | 1729554 |
|----------------------------------------------------------------------|---------|
|                                                                      |      -6 |

The average saved gas is 6.
|   Test Name                                                                           | Gas     |
|---------------------------------------------------------------------------------------|---------|
-| test_getOperatorTotalNonTerminalKeysInvalidPagination(uint64,uint64,uint256,uint256) | 1723913 |
+| test_getOperatorTotalNonTerminalKeysInvalidPagination(uint64,uint64,uint256,uint256) | 1723907 |
|---------------------------------------------------------------------------------------|---------|
|                                                                                       |      -6 |

The average saved gas is 6.
|   Test Name                     | Gas     |
|---------------------------------|---------|
-| test_getValidatorsByOperator() | 1722374 |
+| test_getValidatorsByOperator() | 1722368 |
|---------------------------------|---------|
|                                 |      -6 |

The saved gas is 6.
|   Test Name                         | Gas     |
|-------------------------------------|---------|
-| test_markReadyToDepositValidator() | 1941624 |
+| test_markReadyToDepositValidator() | 1941618 |
|-------------------------------------|---------|
|                                     |      -6 |

The saved gas is 6.
|   Test Name                                          | Gas    |
|------------------------------------------------------|--------|
-| test_updateNextQueuedValidatorIndex(uint64,uint256) | 285459 |
+| test_updateNextQueuedValidatorIndex(uint64,uint256) | 285453 |
|------------------------------------------------------|--------|
|                                                      |     -6 |

The average saved gas is 6.
|   Test Name                                                            | Gas    |
|------------------------------------------------------------------------|--------|
-| test_updateOperatorDetailWithInvalidName(string,uint64,uint64,uint64) | 302797 |
+| test_updateOperatorDetailWithInvalidName(string,uint64,uint64,uint64) | 302791 |
|------------------------------------------------------------------------|--------|
|                                                                        |     -6 |

The average saved gas is 6.
|   Test Name                                                 | Gas    |
|-------------------------------------------------------------|--------|
-| test_updateOperatorDetailWithZeroRewardAddr(string,uint64) | 301505 |
+| test_updateOperatorDetailWithZeroRewardAddr(string,uint64) | 301499 |
|-------------------------------------------------------------|--------|
|                                                             |     -6 |

The average saved gas is 6.
|   Test Name                                              | Gas    |
|----------------------------------------------------------|--------|
-| test_updateOperatorDetails(string,uint64,uint64,uint64) | 315327 |
+| test_updateOperatorDetails(string,uint64,uint64,uint64) | 315321 |
|----------------------------------------------------------|--------|
|                                                          |     -6 |

The average saved gas is 6.
|   Test Name                 | Gas     |
|-----------------------------|---------|
-| test_withdrawnValidators() | 1993128 |
+| test_withdrawnValidators() | 1993122 |
|-----------------------------|---------|
|                             |      -6 |

The saved gas is 6.
|   Test Name                    | Gas     |
|--------------------------------|---------|
-| test_JustToIncreaseCoverage() | 5531119 |
+| test_JustToIncreaseCoverage() | 5421424 |
|--------------------------------|---------|
|                                | -109695 |

The saved gas is 109695.
|   Test Name                                           | Gas   |
|-------------------------------------------------------|-------|
-| test_OnboardOperatorWhenPaused(string,uint64,uint64) | 50497 |
+| test_OnboardOperatorWhenPaused(string,uint64,uint64) | 50496 |
|-------------------------------------------------------|-------|
|                                                       |    -1 |

The average saved gas is 1.
|   Test Name                                                            | Gas    |
|------------------------------------------------------------------------|--------|
-| test_changeSocializingPoolStateWithSomeCoolDown(string,uint64,uint64) | 539233 |
+| test_changeSocializingPoolStateWithSomeCoolDown(string,uint64,uint64) | 539231 |
|------------------------------------------------------------------------|--------|
|                                                                        |     -2 |

The average saved gas is 2.
|   Test Name                                   | Gas     |
|-----------------------------------------------|---------|
-| test_getAllActiveValidators(uint256,uint256) | 2157646 |
+| test_getAllActiveValidators(uint256,uint256) | 2154251 |
|-----------------------------------------------|---------|
|                                               |   -3395 |

The average saved gas is 3395.
|   Test Name                 | Gas     |
|-----------------------------|---------|
-| test_withdrawnValidators() | 2150340 |
+| test_withdrawnValidators() | 2134868 |
|-----------------------------|---------|
|                             |  -15472 |

The saved gas is 15472.
|   Test Name                                                               | Gas   |
|---------------------------------------------------------------------------|-------|
-| testFail_depositSDAsCollateral_withInsufficientApproval(uint256,uint256) | 87525 |
+| testFail_depositSDAsCollateral_withInsufficientApproval(uint256,uint256) | 87518 |
|---------------------------------------------------------------------------|-------|
|                                                                           |    -7 |

The average saved gas is 7.
|   Test Name                    | Gas     |
|--------------------------------|---------|
-| test_JustToIncreaseCoverage() | 2994861 |
+| test_JustToIncreaseCoverage() | 2968403 |
|--------------------------------|---------|
|                                |  -26458 |

The saved gas is 26458.
|   Test Name                                         | Gas    |
|-----------------------------------------------------|--------|
-| test_depositSDAsCollateral(uint128,uint128,uint16) | 137822 |
+| test_depositSDAsCollateral(uint128,uint128,uint16) | 137782 |
|-----------------------------------------------------|--------|
|                                                     |    -40 |

The average saved gas is 40.
|   Test Name                                 | Gas    |
|---------------------------------------------|--------|
-| test_getRemainingSDToBond(uint256,uint256) | 245742 |
+| test_getRemainingSDToBond(uint256,uint256) | 245590 |
|---------------------------------------------|--------|
|                                             |   -152 |

The average saved gas is 152.
|   Test Name                        | Gas    |
|------------------------------------|--------|
-| test_getRewardEligibleSD(uint256) | 251745 |
+| test_getRewardEligibleSD(uint256) | 251569 |
|------------------------------------|--------|
|                                    |   -176 |

The average saved gas is 176.
|   Test Name                      | Gas   |
|----------------------------------|-------|
-| test_sd_eth_converters(uint128) | 40739 |
+| test_sd_eth_converters(uint128) | 40674 |
|----------------------------------|-------|
|                                  |   -65 |

The average saved gas is 65.
|   Test Name              | Gas    |
|--------------------------|--------|
-| test_slashValidatorSD() | 696615 |
+| test_slashValidatorSD() | 696393 |
|--------------------------|--------|
|                          |   -222 |

The saved gas is 222.
|   Test Name                                                    | Gas    |
|----------------------------------------------------------------|--------|
-| test_slashValidatorSD_auctionLotNotCreated_whenNoCollateral() | 213843 |
+| test_slashValidatorSD_auctionLotNotCreated_whenNoCollateral() | 213794 |
|----------------------------------------------------------------|--------|
|                                                                |    -49 |

The saved gas is 49.
|   Test Name                     | Gas    |
|---------------------------------|--------|
-| test_withdraw(uint128,uint128) | 261483 |
+| test_withdraw(uint128,uint128) | 261322 |
|---------------------------------|--------|
|                                 |   -161 |

The average saved gas is 161.
|   Test Name                                         | Gas   |
|-----------------------------------------------------|-------|
-| test_withdraw_revertIfPoolThresholdNotSet(uint256) | 48627 |
+| test_withdraw_revertIfPoolThresholdNotSet(uint256) | 48633 |
|-----------------------------------------------------|-------|
|                                                     |     6 |

The average lost gas is 6.
|   Test Name                                                      | Gas    |
|------------------------------------------------------------------|--------|
-| test_withdraw_reverts_InsufficientSDToWithdraw(uint128,uint128) | 247885 |
+| test_withdraw_reverts_InsufficientSDToWithdraw(uint128,uint128) | 247783 |
|------------------------------------------------------------------|--------|
|                                                                  |   -102 |

The average saved gas is 102.
TestNameGas
NodeELRewardVaultTesttest_withdraw(uint128)-2739
PenaltyTesttest_JustToIncreaseCoverage()-6825
PenaltyTesttest_additionalPenaltyAmount(address,uint256,bytes)6
PenaltyTesttest_calculateMEVTheftPenalty()-38
PenaltyTesttest_updateTotalPenaltyAmount()-94
PermissionedNodeRegistryTesttest_ActivateNodeOperator(uint64,uint64)-6
PermissionedNodeRegistryTesttest_AllocateValidatorsAndUpdateOperatorId()-12
PermissionedNodeRegistryTesttest_DeactivateNodeOperator(uint64,uint64)-6
PermissionedNodeRegistryTesttest_JustToIncreaseCoverage()-200
PermissionedNodeRegistryTesttest_OnboardOperator(string,uint64,uint64)-6
PermissionedNodeRegistryTesttest_addValidatorKeys()-6
PermissionedNodeRegistryTesttest_addValidatorKeysOPCrossingMaxNonTerminalKeys()-6
PermissionedNodeRegistryTesttest_addValidatorKeysWithInsufficientSDCollateral()-6
PermissionedNodeRegistryTesttest_addValidatorKeysWithInvalidKeyCount()-6
PermissionedNodeRegistryTesttest_addValidatorKeysWithMisMatchingInputs()-6
PermissionedNodeRegistryTesttest_getAllActiveValidators(uint256,uint256)-10110
PermissionedNodeRegistryTesttest_getAllActiveValidatorsWithZeroPageNumber(uint256)-6
PermissionedNodeRegistryTesttest_getOperatorTotalNonTerminalKeys(uint64,uint64,uint256,uint256)-6
PermissionedNodeRegistryTesttest_getOperatorTotalNonTerminalKeysInvalidPagination(uint64,uint64,uint256,uint256)-6
PermissionedNodeRegistryTesttest_getValidatorsByOperator()-6
PermissionedNodeRegistryTesttest_markReadyToDepositValidator()-6
PermissionedNodeRegistryTesttest_updateNextQueuedValidatorIndex(uint64,uint256)-6
PermissionedNodeRegistryTesttest_updateOperatorDetailWithInvalidName(string,uint64,uint64,uint64)-6
PermissionedNodeRegistryTesttest_updateOperatorDetailWithZeroRewardAddr(string,uint64)-6
PermissionedNodeRegistryTesttest_updateOperatorDetails(string,uint64,uint64,uint64)-6
PermissionedNodeRegistryTesttest_withdrawnValidators()-6
PermissionlessNodeRegistryTesttest_JustToIncreaseCoverage()-109695
PermissionlessNodeRegistryTesttest_OnboardOperatorWhenPaused(string,uint64,uint64)-1
PermissionlessNodeRegistryTesttest_changeSocializingPoolStateWithSomeCoolDown(string,uint64,uint64)-2
PermissionlessNodeRegistryTesttest_getAllActiveValidators(uint256,uint256)-3395
PermissionlessNodeRegistryTesttest_withdrawnValidators()-15472
SDCollateralTesttestFail_depositSDAsCollateral_withInsufficientApproval(uint256,uint256)-7
SDCollateralTesttest_JustToIncreaseCoverage()-26458
SDCollateralTesttest_depositSDAsCollateral(uint128,uint128,uint16)-40
SDCollateralTesttest_getRemainingSDToBond(uint256,uint256)-152
SDCollateralTesttest_getRewardEligibleSD(uint256)-176
SDCollateralTesttest_sd_eth_converters(uint128)-65
SDCollateralTesttest_slashValidatorSD()-222
SDCollateralTesttest_slashValidatorSD_auctionLotNotCreated_whenNoCollateral()-49
SDCollateralTesttest_withdraw(uint128,uint128)-161
SDCollateralTesttest_withdraw_revertIfPoolThresholdNotSet(uint256)6
SDCollateralTesttest_withdraw_reverts_InsufficientSDToWithdraw(uint128,uint128)-102
----
Total-176111

GO-03

Avoid useless checks

Description

validatorRegistry[0] cannot have status INIZIALIZED

File: PermissionlessNodeRegistry.sol

  
    function onlyInitializedValidator(uint256 _validatorId) internal view {
-       if (_validatorId == 0 || validatorRegistry[_validatorId].status != ValidatorStatus.INITIALIZED) {
+       if (validatorRegistry[_validatorId].status != ValidatorStatus.INITIALIZED) {
            revert UNEXPECTED_STATUS();
        }
    }

https://github.com/code-423n4/2023-06-stader/blob/main/contracts/PermissionlessNodeRegistry.sol#L712-L715

Gas Report:
Using forge snapshot
|   Test Name                    | Gas     |
|--------------------------------|---------|
-| test_JustToIncreaseCoverage() | 5531119 |
+| test_JustToIncreaseCoverage() | 5528910 |
|--------------------------------|---------|
|                                |   -2209 |

The saved gas is 2209.
|   Test Name                                           | Gas   |
|-------------------------------------------------------|-------|
-| test_OnboardOperatorWhenPaused(string,uint64,uint64) | 50497 |
+| test_OnboardOperatorWhenPaused(string,uint64,uint64) | 50496 |
|-------------------------------------------------------|-------|
|                                                       |    -1 |

The average saved gas is 1.
|   Test Name                                   | Gas     |
|-----------------------------------------------|---------|
-| test_getAllActiveValidators(uint256,uint256) | 2157646 |
+| test_getAllActiveValidators(uint256,uint256) | 2149643 |
|-----------------------------------------------|---------|
|                                               |   -8003 |

The average saved gas is 8003.
|   Test Name                                             | Gas     |
|---------------------------------------------------------|---------|
-| test_getAllActiveValidatorsWithZeroPageNumber(uint256) | 1981021 |
+| test_getAllActiveValidatorsWithZeroPageNumber(uint256) | 1980990 |
|---------------------------------------------------------|---------|
|                                                         |     -31 |

The average saved gas is 31.
|   Test Name                                                    | Gas     |
|----------------------------------------------------------------|---------|
-| test_getNodeELVaultAddressForOptOutOperators(uint256,uint256) | 1228311 |
+| test_getNodeELVaultAddressForOptOutOperators(uint256,uint256) | 1228370 |
|----------------------------------------------------------------|---------|
|                                                                |      59 |

The average lost gas is 59.
|   Test Name                         | Gas     |
|-------------------------------------|---------|
-| test_markReadyToDepositValidator() | 2082133 |
+| test_markReadyToDepositValidator() | 2082009 |
|-------------------------------------|---------|
|                                     |    -124 |

The saved gas is 124.
TestNameGas
PermissionlessNodeRegistryTesttest_JustToIncreaseCoverage()-2209
PermissionlessNodeRegistryTesttest_OnboardOperatorWhenPaused(string,uint64,uint64)-1
PermissionlessNodeRegistryTesttest_getAllActiveValidators(uint256,uint256)-8003
PermissionlessNodeRegistryTesttest_getAllActiveValidatorsWithZeroPageNumber(uint256)-31
PermissionlessNodeRegistryTesttest_getNodeELVaultAddressForOptOutOperators(uint256,uint256)59
PermissionlessNodeRegistryTesttest_markReadyToDepositValidator()-124
----
Total-10309

#0 - c4-judge

2023-06-13T21:37:38Z

Picodes marked the issue as grade-b

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