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
Rank: 42/75
Findings: 2
Award: $40.19
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: RaymondFam
Also found by: 0xWaitress, 0xhacksmithh, ChrisTina, DadeKuma, LaScaloneta, Rolezn, SAAJ, Sathish9098, T1MOH, bin2chen, btk, catellatech, ernestognw, fatherOfBlocks, hals, hunter_w3b, jaraxxus, matrix_0wl, mgf15, naman1778, niser93, solsaver, turvy_fuzz
18.5651 USDC - $18.57
Issue | Instances | |
---|---|---|
L-01 | Loss of precision | 9 |
L-02 | Setters should have initial value check | 10 |
L-03 | Use of floating pragma | 1 |
Issue | Instances | |
---|---|---|
NC-01 | Use scientific notation (e.g. 1e18 ) rather than exponentiation (e.g. 10**18 ) | 6 |
NC-02 | Typos | 1 |
NC-03 | Variable names don't follow the Solidity style guide | 1 |
NC-04 | public functions not called by the contract should be declared external instead | 12 |
NC-05 | Let's return default value instead of explicity write it | 1 |
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>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>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
1e18
) rather than exponentiation (e.g. 10**18
)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>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
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
public
functions not called by the contract should be declared external
insteadContracts are allowed to override their parents' functions and change the visibility from external
to public
.
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>Let empty body and consider to comment why.
File: PermissionedNodeRegistry.sol 546 function getCollateralETH() external pure override returns (uint256) { - 547 return 0; 548 }
#0 - c4-judge
2023-06-14T06:04:27Z
Picodes marked the issue as grade-b
🌟 Selected for report: JCN
Also found by: 0x70C9, 0xSmartContract, 0xWaitress, 0xhacksmithh, DavidGiladi, K42, LaScaloneta, Rageur, Raihan, SAAJ, SAQ, SM3_SS, Sathish9098, Tomio, bigtone, c3phas, ernestognw, etherhood, koxuan, matrix_0wl, mgf15, naman1778, niser93, petrichor, piyushshukla, sebghatullah, shamsulhaq123
21.6219 USDC - $21.62
Issue | Instances | |
---|---|---|
GO-01 | Multiplication/division by 2 should use bit shifting | 2 |
GO-02 | Avoid to define variable which are used once | 28 |
GO-03 | Avoid useless checks | 1 |
Test | Name | Gas |
---|---|---|
AuctionTest | test_JustToIncreaseCoverage() | -2096 |
AuctionTest | test_extractNonBidSD(uint256,uint64) | 2 |
NodeELRewardVaultTest | test_withdraw(uint128) | -2739 |
PenaltyTest | test_JustToIncreaseCoverage() | -6825 |
PenaltyTest | test_additionalPenaltyAmount(address,uint256,bytes) | 6 |
PenaltyTest | test_calculateMEVTheftPenalty() | -38 |
PenaltyTest | test_updateTotalPenaltyAmount() | -94 |
PermissionedNodeRegistryTest | test_ActivateNodeOperator(uint64,uint64) | -6 |
PermissionedNodeRegistryTest | test_AllocateValidatorsAndUpdateOperatorId() | -12 |
PermissionedNodeRegistryTest | test_DeactivateNodeOperator(uint64,uint64) | -6 |
PermissionedNodeRegistryTest | test_JustToIncreaseCoverage() | -200 |
PermissionedNodeRegistryTest | test_OnboardOperator(string,uint64,uint64) | -6 |
PermissionedNodeRegistryTest | test_addValidatorKeys() | -6 |
PermissionedNodeRegistryTest | test_addValidatorKeysOPCrossingMaxNonTerminalKeys() | -6 |
PermissionedNodeRegistryTest | test_addValidatorKeysWithInsufficientSDCollateral() | -6 |
PermissionedNodeRegistryTest | test_addValidatorKeysWithInvalidKeyCount() | -6 |
PermissionedNodeRegistryTest | test_addValidatorKeysWithMisMatchingInputs() | -6 |
PermissionedNodeRegistryTest | test_getAllActiveValidators(uint256,uint256) | -10110 |
PermissionedNodeRegistryTest | test_getAllActiveValidatorsWithZeroPageNumber(uint256) | -6 |
PermissionedNodeRegistryTest | test_getOperatorTotalNonTerminalKeys(uint64,uint64,uint256,uint256) | -6 |
PermissionedNodeRegistryTest | test_getOperatorTotalNonTerminalKeysInvalidPagination(uint64,uint64,uint256,uint256) | -6 |
PermissionedNodeRegistryTest | test_getValidatorsByOperator() | -6 |
PermissionedNodeRegistryTest | test_markReadyToDepositValidator() | -6 |
PermissionedNodeRegistryTest | test_updateNextQueuedValidatorIndex(uint64,uint256) | -6 |
PermissionedNodeRegistryTest | test_updateOperatorDetailWithInvalidName(string,uint64,uint64,uint64) | -6 |
PermissionedNodeRegistryTest | test_updateOperatorDetailWithZeroRewardAddr(string,uint64) | -6 |
PermissionedNodeRegistryTest | test_updateOperatorDetails(string,uint64,uint64,uint64) | -6 |
PermissionedNodeRegistryTest | test_withdrawnValidators() | -6 |
PermissionlessNodeRegistryTest | test_JustToIncreaseCoverage() | -111904 |
PermissionlessNodeRegistryTest | test_OnboardOperatorWhenPaused(string,uint64,uint64) | -1 |
PermissionlessNodeRegistryTest | test_changeSocializingPoolStateWithSomeCoolDown(string,uint64,uint64) | -2 |
PermissionlessNodeRegistryTest | test_getAllActiveValidators(uint256,uint256) | -11875 |
PermissionlessNodeRegistryTest | test_getAllActiveValidatorsWithZeroPageNumber(uint256) | -31 |
PermissionlessNodeRegistryTest | test_getNodeELVaultAddressForOptOutOperators(uint256,uint256) | 59 |
PermissionlessNodeRegistryTest | test_markReadyToDepositValidator() | -124 |
PermissionlessNodeRegistryTest | test_withdrawnValidators() | -15472 |
SDCollateralTest | testFail_depositSDAsCollateral_withInsufficientApproval(uint256,uint256) | -7 |
SDCollateralTest | test_JustToIncreaseCoverage() | -26458 |
SDCollateralTest | test_depositSDAsCollateral(uint128,uint128,uint16) | -40 |
SDCollateralTest | test_getRemainingSDToBond(uint256,uint256) | -152 |
SDCollateralTest | test_getRewardEligibleSD(uint256) | -176 |
SDCollateralTest | test_sd_eth_converters(uint128) | -65 |
SDCollateralTest | test_slashValidatorSD() | -222 |
SDCollateralTest | test_slashValidatorSD_auctionLotNotCreated_whenNoCollateral() | -49 |
SDCollateralTest | test_withdraw(uint128,uint128) | -161 |
SDCollateralTest | test_withdraw_revertIfPoolThresholdNotSet(uint256) | 6 |
SDCollateralTest | test_withdraw_reverts_InsufficientSDToWithdraw(uint128,uint128) | -102 |
-- | -- | |
Total | -188990 |
<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
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.
Test | Name | Gas |
---|---|---|
AuctionTest | test_JustToIncreaseCoverage() | -2096 |
AuctionTest | test_extractNonBidSD(uint256,uint64) | 2 |
-- | -- | |
Total | -2094 |
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(); } []
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; }
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
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.
Test | Name | Gas |
---|---|---|
NodeELRewardVaultTest | test_withdraw(uint128) | -2739 |
PenaltyTest | test_JustToIncreaseCoverage() | -6825 |
PenaltyTest | test_additionalPenaltyAmount(address,uint256,bytes) | 6 |
PenaltyTest | test_calculateMEVTheftPenalty() | -38 |
PenaltyTest | test_updateTotalPenaltyAmount() | -94 |
PermissionedNodeRegistryTest | test_ActivateNodeOperator(uint64,uint64) | -6 |
PermissionedNodeRegistryTest | test_AllocateValidatorsAndUpdateOperatorId() | -12 |
PermissionedNodeRegistryTest | test_DeactivateNodeOperator(uint64,uint64) | -6 |
PermissionedNodeRegistryTest | test_JustToIncreaseCoverage() | -200 |
PermissionedNodeRegistryTest | test_OnboardOperator(string,uint64,uint64) | -6 |
PermissionedNodeRegistryTest | test_addValidatorKeys() | -6 |
PermissionedNodeRegistryTest | test_addValidatorKeysOPCrossingMaxNonTerminalKeys() | -6 |
PermissionedNodeRegistryTest | test_addValidatorKeysWithInsufficientSDCollateral() | -6 |
PermissionedNodeRegistryTest | test_addValidatorKeysWithInvalidKeyCount() | -6 |
PermissionedNodeRegistryTest | test_addValidatorKeysWithMisMatchingInputs() | -6 |
PermissionedNodeRegistryTest | test_getAllActiveValidators(uint256,uint256) | -10110 |
PermissionedNodeRegistryTest | test_getAllActiveValidatorsWithZeroPageNumber(uint256) | -6 |
PermissionedNodeRegistryTest | test_getOperatorTotalNonTerminalKeys(uint64,uint64,uint256,uint256) | -6 |
PermissionedNodeRegistryTest | test_getOperatorTotalNonTerminalKeysInvalidPagination(uint64,uint64,uint256,uint256) | -6 |
PermissionedNodeRegistryTest | test_getValidatorsByOperator() | -6 |
PermissionedNodeRegistryTest | test_markReadyToDepositValidator() | -6 |
PermissionedNodeRegistryTest | test_updateNextQueuedValidatorIndex(uint64,uint256) | -6 |
PermissionedNodeRegistryTest | test_updateOperatorDetailWithInvalidName(string,uint64,uint64,uint64) | -6 |
PermissionedNodeRegistryTest | test_updateOperatorDetailWithZeroRewardAddr(string,uint64) | -6 |
PermissionedNodeRegistryTest | test_updateOperatorDetails(string,uint64,uint64,uint64) | -6 |
PermissionedNodeRegistryTest | test_withdrawnValidators() | -6 |
PermissionlessNodeRegistryTest | test_JustToIncreaseCoverage() | -109695 |
PermissionlessNodeRegistryTest | test_OnboardOperatorWhenPaused(string,uint64,uint64) | -1 |
PermissionlessNodeRegistryTest | test_changeSocializingPoolStateWithSomeCoolDown(string,uint64,uint64) | -2 |
PermissionlessNodeRegistryTest | test_getAllActiveValidators(uint256,uint256) | -3395 |
PermissionlessNodeRegistryTest | test_withdrawnValidators() | -15472 |
SDCollateralTest | testFail_depositSDAsCollateral_withInsufficientApproval(uint256,uint256) | -7 |
SDCollateralTest | test_JustToIncreaseCoverage() | -26458 |
SDCollateralTest | test_depositSDAsCollateral(uint128,uint128,uint16) | -40 |
SDCollateralTest | test_getRemainingSDToBond(uint256,uint256) | -152 |
SDCollateralTest | test_getRewardEligibleSD(uint256) | -176 |
SDCollateralTest | test_sd_eth_converters(uint128) | -65 |
SDCollateralTest | test_slashValidatorSD() | -222 |
SDCollateralTest | test_slashValidatorSD_auctionLotNotCreated_whenNoCollateral() | -49 |
SDCollateralTest | test_withdraw(uint128,uint128) | -161 |
SDCollateralTest | test_withdraw_revertIfPoolThresholdNotSet(uint256) | 6 |
SDCollateralTest | test_withdraw_reverts_InsufficientSDToWithdraw(uint128,uint128) | -102 |
-- | -- | |
Total | -176111 |
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(); } }
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.
Test | Name | Gas |
---|---|---|
PermissionlessNodeRegistryTest | test_JustToIncreaseCoverage() | -2209 |
PermissionlessNodeRegistryTest | test_OnboardOperatorWhenPaused(string,uint64,uint64) | -1 |
PermissionlessNodeRegistryTest | test_getAllActiveValidators(uint256,uint256) | -8003 |
PermissionlessNodeRegistryTest | test_getAllActiveValidatorsWithZeroPageNumber(uint256) | -31 |
PermissionlessNodeRegistryTest | test_getNodeELVaultAddressForOptOutOperators(uint256,uint256) | 59 |
PermissionlessNodeRegistryTest | test_markReadyToDepositValidator() | -124 |
-- | -- | |
Total | -10309 |
#0 - c4-judge
2023-06-13T21:37:38Z
Picodes marked the issue as grade-b