Stader Labs - Tomio'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: 53/75

Findings: 1

Award: $21.62

Gas:
grade-b

🌟 Selected for report: 0

🚀 Solo Findings: 0

Awards

21.6219 USDC - $21.62

Labels

bug
G (Gas Optimization)
grade-b
sponsor acknowledged
G-04

External Links

Title: empty constructor

Proof of Concept: NodeELRewardVault.sol#L14

Recommended Mitigation Steps: Remove if unused for gas saving


Title: Using msg.sender directly is more effective

Proof of Concept: OperatorRewardsCollector.sol#L47 SDCollateral.sol#L44 SDCollateral.sol#L59

Recommended Mitigation Steps: delete operator and use msg.sender directly instead of caching it.

uint256 amount = balances[msg.sender]; balances[msg.sender] -= amount;

Title: function getTotalQueuedValidatorCount() gas improvement on returning value

Proof of Concept: PermissionedNodeRegistry.sol#L487

Recommended Mitigation Steps: by set totalQueuedValidators in returns L#486 and delete L#487 can save gas

function getTotalQueuedValidatorCount() external view override returns (uint256 totalQueuedValidators) { //@audit-info: set here

Title: function getOperatorTotalNonTerminalKeys() gas improvement on returning value

Proof of Concept: PermissionedNodeRegistry.sol#L533

Recommended Mitigation Steps: by set totalNonWithdrawnKeyCount in returns L#526 and delete L#533 can save gas

function getOperatorTotalNonTerminalKeys( address _nodeOperator, uint256 _startIndex, uint256 _endIndex ) public view override returns (uint64 totalNonWithdrawnKeyCount) { //@audit-info: set here

Title: Avoid unnecessary memory allocation and copy

Proof of Concept: PermissionlessPool.sol#L276-L286

Recommended Mitigation Steps: instead of allocating a new bytes array and copying the values one by one, you can use abi.encodePacked to directly pack the values into a bytes array.

Consider change it to:

function to_little_endian_64(uint256 _depositAmount) internal pure returns (bytes memory) { uint64 value = uint64(_depositAmount / 1 gwei); return abi.encodePacked(value); }

Title: Avoid unnecessary variable assignments

Proof of Concept: PoolUtils.sol#L256-L257

Recommended Mitigation Steps: Instead of assigning usersETH and collateralETH to variables, use it directly.

Consider change it to:

function calculateRewardShare(uint8 _poolId, uint256 _totalRewards) external view override returns ( uint256 userShare, uint256 operatorShare, uint256 protocolShare ) { uint256 TOTAL_STAKED_ETH = staderConfig.getStakedEthPerNode(); uint256 protocolFeeBps = getProtocolFee(_poolId); uint256 operatorFeeBps = getOperatorFee(_poolId); uint256 _userShareBeforeCommission = (_totalRewards * (TOTAL_STAKED_ETH - getCollateralETH(_poolId))) / TOTAL_STAKED_ETH; //@audit-info: set here protocolShare = (protocolFeeBps * _userShareBeforeCommission) / staderConfig.getTotalFee(); operatorShare = (_totalRewards * getCollateralETH(_poolId)) / TOTAL_STAKED_ETH; //@audit-info: set here operatorShare += (operatorFeeBps * _userShareBeforeCommission) / staderConfig.getTotalFee(); userShare = _totalRewards - protocolShare - operatorShare; }

#0 - c4-judge

2023-06-13T21:39:17Z

Picodes marked the issue as grade-a

#1 - c4-sponsor

2023-06-21T13:15:00Z

manoj9april marked the issue as sponsor acknowledged

#2 - c4-judge

2023-07-03T13:51:28Z

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