Skip to content

New Compounding Staking Strategy post Pectra upgrade #2559

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

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

Conversation

naddison36
Copy link
Collaborator

@naddison36 naddison36 commented Jun 30, 2025

A new staking strategy that uses merkle proofs to verify the operations of validators running on the beacon chain.

Objectives

  • Consolidation of validators to reduce the operation costs.
  • Consolidation to a single staking strategy to reduce the Vault gas costs.
  • Better protection against front-running of deposits to a new validator.
  • On-chain withdrawals reduces the dependency on third party node operators managing validator private keys.
  • Merkle proofs of the beacon chain is much more secure than Oracles providing validator balances.

Links

Relevant Pectra Release changes

  • EIP-6110: On-Chain Validator Deposits
  • EIP-7002: Execution layer triggerable withdrawals
  • EIP-7251 Increase the MAX_EFFECTIVE_BALANCE
  • EIP-7685 General purpose execution layer requests

Contracts

The scope of the contracts of this build is

  • CompoundingStakingSSVStrategy is the new staking contract.
  • CompoundingValidatorManager is inherited by CompoundingStakingSSVStrategy
  • BeaconProofs called from CompoundingValidatorManager

The following libraries have also be built

  • BeaconRoots
  • BeaconProofsLib
  • PartialWithdrawal
  • Merkle
  • Endian
oethContracts

Hoodi

Contract Name Address
CompoundingStakingSSVStrategyProxy 0x840081c97256d553A8F234D469D797B9535a3B49
BeaconProofs 0x1df1B71c5204DB0e5f3f7035A57285D53454C710
OETHVaultProxy 0xD0cC28bc8F4666286F3211e465ecF1fe5c72AC8B
OETHProxy 0xbebDca6eF7452953e0AB5cebE2A174B71208B13a
NativeStakingSSVStrategyProxy 0x95DdB082cf2b8Bb4a4A358A8817a848cE32e446b
NativeStakingFeeAccumulatorProxy 0x27281CE00322Ee8b7C078788Fb624D051F5F7689

States

stakingValueTransitions image

Beacon chain data

BeaconBlock
├── slot: Slot
├── proposer_index: ValidatorIndex
├── parent_root: Root
├── state_root: Root  # Merkle root of BeaconState
│   └── BeaconState  # Does not include all fields
│       ├── validators: List[Validator, MAX_VALIDATORS]
│       │   ├── pubkey: BLSPubkey
│       │   ├── withdrawal_credentials: Bytes32
│       │   ├── effective_balance: uint64
│       │   ├── slashed: boolean
│       │   ├── activation_epoch: Epoch
│       │   ├── exit_epoch: Epoch
│       │   └── withdrawable_epoch: Epoch
│       ├── balances: List[Gwei, MAX_VALIDATORS]
│       ├── eth1_data: Eth1Data
│       │   ├── deposit_root: Root
│       │   ├── deposit_count: uint64
│       │   └── block_hash: Hash32
│       ├── eth1_deposit_index: uint64
│       └── deposit_requests_start_index: uint64
│       └── deposit_balance_to_consume: Gwei
│       └── pending_deposits: List[PendingDeposit, PENDING_DEPOSITS_LIMIT]
└── body_root: Root
    └── BeaconBlockBody  # Does not include all fields
        ├── eth1_data: Eth1Data
        ├── attestations: List[Attestation, MAX_ATTESTATIONS]
        ├── slashings: List[Slashing, MAX_SLASHINGS]
        ├── sync_aggregate: SyncAggregate
        ├── deposits: List[Deposit, MAX_DEPOSITS]
        ├── execution_payload: Root # at index 9
        │   └── ExecutionPayload
        │       ├── block_number: uint64  # at index 6
        └── requests: List[Request, MAX_REQUESTS]
            ├── type: uint8 (deposit, exit, consolidation)
            ├── data: Bytes (e.g., DepositData, ExitData)
            └── source: Hash32 (EL block hash)

Beacon Container Specs

To get the consolidate beacon chain specification for the last Pectra release

git clone https://github.com/ethereum/consensus-specs.git
cd consensus-specs
make pyspec

build/lib/eth2spec/electra/mainnet.py will have the full, consolidated changes. This includes all the beacon chain container definitions. eg BeaconBlock, BeaconBlockBody, BeaconState, Validator...

Processes

Register a SSV validator

oethProcesses-register

Initial deposit to a new validator

oethProcesses-deposit-new

Deposit more to existing validator

oethProcesses-deposit-existing

Verify validator

oethProcesses-verify-validator

Verify deposit to validator

oethProcesses-verify-deposit

Update strategy balances

oethProcesses-verify-balances

Withdrawals

oethProcesses-withdraw

Admin

oethProcesses-admin

Build

Keeping the contract under 24Kb has been a problem during development. The following will show the contract size of the compile contracts including CompoundingStakingSSVStrategy.

export CONTRACT_SIZE=true
npx hardhat compile

Testing

Unit Tests

Are main strategy unit tests in contracts/test/strategies/compoundingSSVStaking.js

There are also unit tests of the beacon merkle proofs in contracts/test/beacon/beaconProofs.js

yarn test

Fork Tests

There are no fork tests of the strategy as its hard to mock the beacon chain. But there are fork tests of various beacon chain contracts and libraries. These includes:

  • contracts/test/beacon/beaconConsolidation.mainnet.fork-test.js
  • contracts/test/beacon/beaconProofs.mainnet.fork-test.js
  • contracts/test/beacon/beaconRoots.mainnet.fork-test.js
  • contracts/test/beacon/partialWithdrawal.mainnet.fork-test.js

Hoodi Testnet

Hoodi faucet: https://hoodi-faucet.pk910.de/
SSV faucet: https://faucet.ssv.network/

Contracts have been deployed to Hoodi with deployment scripts in contracts/deploy/hoodi

yarn run deploy:hoodi
npx hardhat etherscan-verify --network hoodi --api-url https://api-hoodi.etherscan.io

Hoodi testing using Hardhat tasks

# Set `DEFENDER_API_KEY` and `DEFENDER_API_SECRET` in your `.env` file for the Hoodi Defender Relayer

# Get some WETH
npx hardhat depositWETH --amount 64 --network hoodi

# Mint OETH with approval
npx hardhat mint --asset WETH --amount 64 --symbol OETH --network hoodi

# Deposit old to native staking strategy
npx hardhat depositToStrategy --symbol OETH --strategy NativeStakingSSVStrategyProxy --assets WETH --amounts 64 --network hoodi

# Check the WETH was deposited
npx hardhat snapStaking --network hoodi

# Set the amount of Ether than can be staked before needing a reset
npx hardhat setStakeETHThreshold --amount 500 --network hoodi
# Reset the stakeETHTally if needed
npx hardhat resetStakeETHTally --network hoodi

# Register an old sweeping validator
npx hardhat registerValidators --days 30 --validators 1 --network hoodi

# stake to the old sweeping validator
npx hardhat stakeValidators --network hoodi

# Check the validator is active
npx hardhat snapStaking --network hoodi

# Deposit to new compounding staking strategy
npx hardhat depositToStrategy --symbol OETH --strategy CompoundingStakingSSVStrategyProxy --assets WETH --amounts 64 --network hoodi

# Check WETH in the new strategy
npx hardhat snapStakingStrat --network hoodi

# Call P2P API to request a new compounding SSV validator
npx hardhat requestNewValidator --network hoodi

# Request the validator via the staking contract
npx hardhat registerValidator --uuid 76be0ee1-de4e-4e4c-8904-9981e25e42a4 --operatorids 127,125,129,131 --network hoodi

# Deposit to a new validator
npx hardhat stakeValidator --pubkey 0x90244afab93283dcf1175c1eaa5c5eeab3acc3f70462a79ff59d06386a3c16690359aaa8905d3266420cda47a753af17 --sig 0x87a78e679db819ec982ea8b033f7aedcac19e2c9dae5c7db30b542969c380712da326d9dc88a3c5541c2e09e963522a112bc04b4ab1915feefcc9cea3c6cb33541df64130ccf436dac4ad24f165006dcb7abc1f65296074ec11d34ecb1c6fb7e --amount 32 --network hoodi

# Verify the validator
# make sure BEACON_PROVIDER_URL is pointing to the Hoodi beacon chain
npx hardhat verifyValidator --index 1187295 --slot 968894 --network hoodi
npx hardhat getValidator --index 1187295 --network hoodi

# Verify the deposit
npx hardhat verifyDeposit --slot 969405 --root 0x53bcd3650a5d66c668e92c34ccfb56f60adcf27180c44655ee199616af220309 --network hoodi

# Snap the ETH balance
npx hardhat snapBalances --network hoodi

# Verify the balances
npx hardhat verifyBalances --root 0x07929f2af14fafc0de4e1f93d429844eef3d5d8685816fcb33bef76569de5515 --network hoodi

# Partial withdrawal from a validator
npx hardhat withdrawValidator --amount 5 --pubkey 0x8f4429ea2bd4228f0ca74b4e232b03f6e0ef5bd2420951240dfe2acc423944aab40530b5f3165f231600e1a90fed3d4e --network hoodi

Deployment

Hoodi

 export DEPLOYER_PK=
yarn run deploy:hoodi

npx hardhat etherscan-verify --contract-name CompoundingStakingSSVStrategy --network hoodi --api-url https://api-hoodi.etherscan.io
npx hardhat etherscan-verify --contract-name BeaconProofs --network hoodi --api-url https://api-hoodi.etherscan.io

npx hardhat tenderlyUpload --name CompoundingStakingSSVStrategy --network hoodi
npx hardhat tenderlyUpload --name BeaconProofs --network hoodi

Code Change Checklist

To be completed before internal review begins:

  • The contract code is complete
  • Executable deployment file
  • Fork tests that test after the deployment file runs
  • Unit tests *if needed
  • The owner has done a full checklist review of the code + tests

Internal review:

  • Two approvals by internal reviewers

Copy link

github-actions bot commented Jun 30, 2025

Warnings
⚠️ 👀 This PR needs at least 2 reviewers

Generated by 🚫 dangerJS against fd21ed6

Copy link

codecov bot commented Jun 30, 2025

Codecov Report

❌ Patch coverage is 86.34921% with 43 lines in your changes missing coverage. Please review.
✅ Project coverage is 40.54%. Comparing base (b3b0ebb) to head (fd21ed6).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
.../strategies/NativeStaking/ValidatorRegistrator.sol 0.00% 24 Missing ⚠️
contracts/contracts/beacon/BeaconConsolidation.sol 0.00% 10 Missing ⚠️
...es/NativeStaking/CompoundingStakingSSVStrategy.sol 89.58% 5 Missing ⚠️
...gies/NativeStaking/CompoundingValidatorManager.sol 98.05% 3 Missing ⚠️
contracts/contracts/beacon/BeaconProofsLib.sol 97.56% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2559      +/-   ##
==========================================
+ Coverage   38.26%   40.54%   +2.28%     
==========================================
  Files         112      121       +9     
  Lines        5331     5628     +297     
  Branches     1412     1494      +82     
==========================================
+ Hits         2040     2282     +242     
- Misses       3289     3344      +55     
  Partials        2        2              

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

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

naddison36 and others added 8 commits July 10, 2025 09:19
* WIP: adjust test for compoundingSSVStrategy

* fix: type conversion for deposit amount in CompoundingValidatorManager

* Revert "fix: type conversion for deposit amount in CompoundingValidatorManager"

This reverts commit 0e49f91.

* fix: type conversion for deposit amount in CompoundingValidatorManager

* refactor: clean up unused variables and comments in compoundingSSVStaking tests

* test: fix test for initial setup

* test: update tests for adding source strategy and governor interactions

* test: add unit tests for removing validators in Compounding SSV Staking Strategy

* test: add deposit and withdraw functionality tests for Compounding SSV Staking Strategy

* test: add unit tests for pausing strategy and withdrawing assets in Compounding SSV Staking Strategy

* test: add unit test for collecting rewards in Compounding SSV Staking Strategy
* Setup Hoodi network

* lint
Stripped out redundant unit tests
* enough WETH to add it up to 32 so it can be staked. For that reason the check is left out.
*
* WETH sitting on the strategy won't interfere with the accounting since accounting only operates on ETH.
*/
emit Deposit(_asset, address(0), _amount);
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

I like this simplification 👍

Copy link
Member

@sparrowDom sparrowDom left a comment

Choose a reason for hiding this comment

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

Some comments inline. Also 1 other issue to address that might be lost in the thread previously

@sparrowDom
Copy link
Member

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants