Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SSV Native staking #2015

Open
wants to merge 48 commits into
base: master
Choose a base branch
from
Open

SSV Native staking #2015

wants to merge 48 commits into from

Conversation

sparrowDom
Copy link
Member

@sparrowDom sparrowDom commented Apr 4, 2024

Contracts

Changes

  • Added new NativeStakingSSVStrategy that takes WETH from the Vault and stakes ETH into SSV validators.
  • Added FeeAccumulator to receive ETH execution rewards.
  • Changes BaseHarvester so it can collect WETH and send it straight to the Dripper

oethContracts

Diagrams

NativeStakingSSVStrategy

NativeStakingSSVStrategyHierarchy

NativeStakingSSVStrategySquashed

FeeAccumulator

FeeAccumulatorHierarchy

FeeAccumulatorSquashed

Processes

Value Flows

The flow of native currency (ETH) and tokens between the wallets and contracts.

oethValueFlows-native-staking

Contract calls

oethProcesses

Testing

Unit Tests

Unit tests are in contracts/test/strategies/nativeSSVStaking.js

yarn run test

Holesky fork tests

Holesky fork tests are in contracts/test/strategies/nativeSsvStaking.holesky.fork-test.js

yarn run node:hol
yarn run test:hol-fork

Mainnet fork tests

Mainnet fork tests are in contracts/test/strategies/nativeSsvStaking.fork-test.js

yarn run node
yarn run test:fork

Deployment

Operations

Hardhat tasks

@sparrowDom sparrowDom self-assigned this Apr 4, 2024
Copy link

github-actions bot commented Apr 4, 2024

Warnings
⚠️ 👀 This PR needs at least 2 reviewers

Generated by 🚫 dangerJS against 7eb135b

Copy link

codecov bot commented Apr 4, 2024

Codecov Report

Attention: Patch coverage is 94.94382% with 9 lines in your changes are missing coverage. Please review.

Project coverage is 62.24%. Comparing base (fdb0ba0) to head (e1ff728).
Report is 22 commits behind head on master.

❗ Current head e1ff728 differs from pull request most recent head 579a93c. Consider uploading reports for the commit 579a93c to get more accurate results

Files Patch % Lines
contracts/contracts/oracle/OETHFixedOracle.sol 0.00% 4 Missing ⚠️
.../strategies/NativeStaking/ValidatorRegistrator.sol 94.44% 3 Missing ⚠️
...ategies/NativeStaking/NativeStakingSSVStrategy.sol 98.07% 1 Missing ⚠️
...s/strategies/NativeStaking/ValidatorAccountant.sol 98.14% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2015      +/-   ##
==========================================
+ Coverage   60.92%   62.24%   +1.31%     
==========================================
  Files          58       64       +6     
  Lines        2974     3181     +207     
  Branches      774      829      +55     
==========================================
+ Hits         1812     1980     +168     
- Misses       1159     1198      +39     
  Partials        3        3              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

EXIT_COMPLETE // validator has funds withdrawn to the EigenPod and is removed from the SSV
}

event RegistratorAddressChanged(address oldAddress, address newAddress);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this has been a pattern used for a while in Origin dollar, but do you need both the old and new addresses in change events? Can we just have the new address?


balance = activeDepositedValidators * 32 ether;
balance += beaconChainRewardWETH;
balance += FEE_ACCUMULATOR_ADDRESS.balance;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we want to include the MEV rewards in the FeeAccumulator if those are going to be harvested and sent to through the dripper.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks this is a nice catch. I see I haven't given it enough though as to how (W)ETH should be handled.
Lets continue discussion here so we have it all in 1 place: https://www.notion.so/originprotocol/How-should-accounting-function-handle-ETH-after-accounting-4ed3a9352c0f4fc8aa6bb866022a25cb

/// @dev Governor that can manually correct the accounting
address public accountingGovernor;
/// @dev Strategist that can pause the accounting
address public strategist;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Best to read the strategist from the Vault rather than duplicating the maintenance in the strategy. This is what OETH AMO strategy does

    modifier onlyStrategist() {
        require(
            msg.sender == IVault(vaultAddress).strategistAddr(),
            "Caller is not the Strategist"
        );
        _;
    }

naddison36 and others added 10 commits April 22, 2024 17:35
* Update Natspec

* Generated docs for native eth strategy

* Prettier and linter
Fixed spelling of ValidatorAccountant events
Implemented depositSSV

* Updated Natspec
Moved MAX_STAKE on ValidatorAccountant to a constant

* Removed strategist from strategy as its already maintained in the Vault

* Fix compilation error

* Fix unit tests

* fix linter
* Added OETH process diagram with functions calls for native staking

* Native Staking Strategy now hold consensus rewards at ETH
FeeAccumulator now holds execution rewards as ETH
Removed WETH immutable from FeeAccumulator
Converted custom errors back to require with string
collect rewards now converts ETH to WETH at harvest
checkBalance is now validators * 32 plus WETH balance from deposits
Renamed beaconChainRewardsWETH to consensusRewards
Fixed bug in stakeETH that was converting all WETH to ETH
* Fixed native staking deployment since the strategist is got from the vault

* Refactor of some Native Staking events
Refactor of Native Staking unit tests

* Renamed AccountingBeaconChainRewards to AccountingConsensusRewards
Accounting updated to handle zero ETH from the beacon chain

* fixed bug not accounting for previous consensus rewards
Blow fuse if ETH balance < previous consensus rewards

* Pause collectRewardTokens and doAccounting on accounting failure.

Validated asset on deposit to Native Staking Strategy.

Moved depositSSV from NativeStakingSSVStrategy to ValidatorRegistrator

moved onlyStrategist modified and VAULT_ADDRESS immutable from ValidatorAccountant to ValidatorRegistrator

manuallyFixAccounting changed to use whenPaused modifier

made fuseIntervalEnd inclusive

Natspec updates

refactoring of native staking unit tests
* add basic steps to deploy OETH to holesky

* prettier

* minor change

* holesky deployment ifles
holesky deployment files

* add holesky deployment files

* minor fix

* minor fixes

* make the fork tests run on Holesky

* add some more tests

* testing SSV staking on Holesky

* refactor where deployment files are located

* more progress on deployment

* add deposit to validator deployment files

* remove log

* prettier

* lint

* move file

* SSV cluster info (#2036)

* add ability to fetch SSV cluster information

* prettier
* manuallyFixAccounting now uses delta values and only callable by the strategist
manuallyFixAccounting calls doAccounting to check the fuse is still not blown
Removed accountingGovernor

* Added pauseOnFail param to internal _doAccounting
Increased the allowed delta values of manuallyFixAccounting

* ran prettier
uint256 public constant MAX_STAKE = 32 ether;

/// @notice Keeps track of the total consensus rewards swept from the beacon chain
uint256 public consensusRewards = 0;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In Solidity, variables are automatically initialized to zero. Therefore, explicitly initializing variables to zero is not mandatory and can be skipped.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, fixed here: 8bf9a26

validators[i].depositDataRoot
);

activeDepositedValidators += 1;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could save on some gas by adding the validators.length once instead of doing a +1 on every interaction..

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great gas savings thanks: fd4f2d2


// send the ETH that is from fully withdrawn validators to the Vault
if (newSweptETH >= MAX_STAKE) {
uint256 fullyWithdrawnValidators = newSweptETH / MAX_STAKE;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This safe division could use unchecked

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed, thanks

require(msg.sender == STRATEGY, "Caller is not the Strategy");

eth = address(this).balance;
if (eth > 0) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have a couple of superior to zero checks, it's more gas-efficient to use a not zero check.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tested this with the hardhat-gas-reporter and Remix and didn't see a noticeable difference in gas usage.

IWETH9(WETH_TOKEN_ADDRESS).withdraw(requiredETH);

// For each validator
for (uint256 i = 0; i < validators.length; ) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

validators.length should be cached outside of the loop.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks, fixed here: fd4f2d2

@DanielVF DanielVF added the contracts Works related to contracts label May 8, 2024
* adding defender action task

* fix unit tests setup

* update the registrator address of the native staking contract

* fix up the operate validators script so that it works for native staking on holesky

* also add the gitignore

* add ability to exit if staking contract is paused
@sparrowDom sparrowDom marked this pull request as ready for review May 9, 2024 12:37
naddison36 and others added 12 commits May 10, 2024 08:16
* Fixed resolveAsset

* Fixed validator fork tests
Added error func sigs to ISSVNetwork

* Fixed harvester behaviour tests

* Fix global hooks

* Fix tooling

---------

Co-authored-by: Shahul Hameed <10547529+shahthepro@users.noreply.github.com>
* add payable to fee accumulator

* remove zero initializers

* some gas savings
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
contracts Works related to contracts
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants