Everstake is a responsible validator trusted by 625k+ users across 70+ blockchain networks created by engineers for the entire community in 2018.
Everstake 0.1+ ETH staking solution is a protocol that allows users to deposit amounts that can be less than 32 ETH. Once users’ deposits exceed 32 ETH, a new validator is created, and the users can start earning rewards. The staking rewards are restaked automatically, and the users’ pool shares are increased.
Everstake also engaged Ackee Blockchain to perform a fix review of the second audit revision on commit
We began our review by using static analysis tools, namely Woke. We then took a deep dive into the logic of the contracts. For testing and fuzzing, we have involved a Woke testing framework.
During the review, we paid special attention to:
- ensuring the accounting arithmetic of the system is correct
testing whether no unstaking path reverts
- testing whether users can’t withdraw more than they have deposited (+ rewards)
- analyzing the management of the validators
- detecting possible ETH call reentrancies in the code
- ensuring access controls are not too relaxed or too strict
- testing if rewards are distributed according to users’ shares
- analyzing that the contracts use proper data structures for storing deposits, withdrawals, etc.
- analyzing the upgradeability pattern (storage collision, access control, etc.)
- analyzing whether the withdrawal credentials are created correctly
- looking for common issues such as data validation.
We followed the methodology established in the previous revision:
- We focused on manually reviewing all the changes and wrote additional tests in Woke.
- We wrote a new simple differential fuzz test for the quick sort function and made it available in the Woke appendix.
- We also utilized Woke for static analysis, which was mainly useful for analysis of reentrancies.
During the review, we had similar objectives as in the previous revision; additionally, we focused on:
- verifying that all the fixes were applied correctly
- withdrawing edge-case amounts (like very small values, values equalling all shares, etc.)
- minting shares and subsequent reward distribution
- reviewing the integer-division-based loss of precision introduced in the amount-share conversions
- reviewing the view functions (mainly _simulateAutocompound)
- reviewing the new ordering logic of the validators
- analyzing transaction ordering and front-running opportunities
- reviewing the new upgradeability pattern.
The audit has been performed on commit
60688fc, and the scope was the entire contracts folder:
contracts/ ├── Accounting.sol ├── AutocompoundAccounting.sol ├── Governor.sol ├── Pool.sol ├── RewardsTreasury.sol ├── TreasuryBase.sol ├── WithdrawTreasury.sol ├── Withdrawer.sol ├── common │ └── Errors.sol ├── interfaces │ ├── IAccounting.sol │ ├── IDepositContract.sol │ ├── IPool.sol │ ├── IRewardsTreasury.sol │ └── ITreasuryBase.sol ├── lib │ ├── UnstructuredRefStorage.sol │ └── UnstructuredStorage.sol
├── structs │ ├── ValidatorList.sol │ └── WithdrawRequests.sol └── utils ├── Math.sol └── OwnableWithSuperAdmin.sol
The review was done on commit
35f9b56 and the files in scope were the same as in the previous audit. Since the last audit 45 new commits were made, a large number of those were fix commits and refactoring commits. The most notable changes were:
- adding upgradeability to treasuries
- making interchange optional
- modifying the ordering logic of the validators
- adding gas optimizations
- improving naming of the variables and adding comments
- fixing the issues found in the previous audit.
We performed a fix review of the second audit revision on commit
No critical severity issues were found.
H1: _simulateAutocompound can revert
H2: DoS due to 0 pending deposits
H3: Partial DoS due to interchange
H4: DoS due to underflow
M1: Missing whenWithdrawActive modifier
M2: Call to depositedBalanceOf reverts
L1: Withdraw request array monotonically grows
L2: Lack of 2-phase role transfers
L3: Exiting validators can revert
L4: Replacing validators lacks validation
L5: Validation of owner in treasuries
L6: Data validation in initialize functions
L7: Incorrect return value of _simulateAutocompound
L8: Upgradeable contract constructor without initializer
L9: Insufficient data validation when composing contracts
W1: Usage of solc optimizer
W2: Dead code in _autoCompoundUserBalance
W3: Unchecked return of _update
W4: Lack of contract prefix in storage position
W5: Pool fee can be set extremely high
I1: Used library
I2: Comparison with role outside modifier
I3: Function always returns true
I4: Lack of logging in setters
I5: Code and comment discrepancy
I6: Lack of documentation
No critical severity issues were found.
H5: Withdrawal of autocompoundBalanceOf amount reverts
M3: simulateAutocompound checks only balance diff
L10: Pending deposited can’t be withdrawn
L11: Lack of call to disableInitializers()
L12: Lack of 0 shares check in simulateAutocompound
L13: Lack of 0 shares check in feeBalance
W6: Withdraw can return by 1 wei more than requested
W7: Withdrawal revert due to rounding
W8: unstakePending and activateBalance can revert due to bad timing
I7: Code duplication for ownership
I8: Typos in code and comments
I9: Array length validation
Our review resulted in 26 findings, ranging from Info to High severity. The highest severity issues were related to denial of service and the inability to view the state of the protocol due to underflow reverts.
Overall, we do not recommend deploying the current version of the protocol. During the audit, we discovered a couple of issues that caused the protocol to revert, although the state was achieved only through normal, non-malicious transactions.
This implies that the protocol is not sufficiently tested. Additionally, we found the documentation to be lacking, so we dedicated a separate informational issue to this.
At the same time, we would like to acknowledge that the development team was able to find some of the issues independently of our review (i.e., both the teams found it independently of each other), most notably, it was H3. Additionally, during the audit, we observed multiple clever design decisions in the Pool and Accounting contracts.
However, due to the high number of issues, including high-severity ones, some work needs to be done to make the protocol production-ready.
Due to the high number of issues, lack of documentation and the fact that the protocol reverted in certain scenarios, our auditing processes were slowed down. As such, we do not feel certain that the protocol will be fully secure after the fixes are applied. We strongly recommend pursuing another audit round, though a shorter one, to ensure that the protocol is secure.
Our second review resulted in 12 findings, ranging from Info to High severity. The highest severity issue related to integer-division-based error, which in certain protocol states resulted in withdrawal reverts and thus caused temporal lock of users’ funds.
The code quality in the second revision was significantly improved. The code was more readable (mainly due to the use of better names for variables) and the documentation was better. Almost all issues from the previous revision were fixed.
Based on the observations made during the review, we recommend focusing on the following high-level objectives:
- The documentation is still lacking and could be improved.
- Due to the occurrence of another rounding-based issue, we recommend fuzzing.
- Protocol to ensure that no other subtle off-by-one errors are present.
- Another bug was uncovered in the _simulateAutocompound function, it is recommended to rethink the approach of writing the simulations and use a more organized and structured approach.
- Avoid overly complicated and overengineered solutions like in the case of the reorderings and replacements of validators, such optimizations are not usually worth it in the long run.
- Fix all the reported issues.
We consider all the issues to be fixed correctly. We believe that by fixing the H5 rounding issue no other rounding issues that would cause reverts in main user flows are present. Yet still we recommend fuzzing the protocol to analyze the protocol’s behavior in random scenarios and protocol states.
We were delighted to audit Everstake and look forward to working with them again.