Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 17 additions & 16 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
HOLESKY_ETH_NODE_URL=
HOLESKY_OWNER_PRIVATE_KEY=
MAINNET_ETH_NODE_URL=
MAINNET_OWNER_PRIVATE_KEY=
GAS_PRICE=
GAS=
# === Network RPC URLs ===
HOODI_RPC_URL=
MAINNET_RPC_URL=

# === Private Keys (deployer/owner) ===
HOODI_PRIVATE_KEY=
MAINNET_PRIVATE_KEY=

# === SSV Token Addresses (per-network) ===
HOODI_SSVTOKEN_ADDRESS=
MAINNET_SSVTOKEN_ADDRESS=

# === Etherscan Verification ===
ETHERSCAN_KEY=
NODE_PROVIDER_KEY=
MINIMUM_BLOCKS_BEFORE_LIQUIDATION=100800
MINIMUM_LIQUIDATION_COLLATERAL=200000000
OPERATOR_MAX_FEE_INCREASE=3
DECLARE_OPERATOR_FEE_PERIOD=259200 # 3 days
EXECUTE_OPERATOR_FEE_PERIOD=345600 # 4 days
VALIDATORS_PER_OPERATOR_LIMIT=500
QUORUM_BPS=6700
DEFAULT_ORACLE_IDS=1,2,3,4
SSVTOKEN_ADDRESS=

# === Legacy (kept for backward compat, prefer deployments/<env>/config.json) ===
# GAS_PRICE=
# GAS=
114 changes: 50 additions & 64 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,92 +20,78 @@ sizes:
npx hardhat compile --force
npx tsx ./scripts/contract-sizes.ts

# Deploy a specific module contract (e.g., SSVOperators, SSVClusters)
# Args: module=<name> network=<name> [args=constructor_args]
deploy-module module network *args:
# === Env-based Workflows ===
# All env-based recipes take `env` as the first arg.
# The network is auto-resolved from the env name (hoodi-* -> hoodi, mainnet -> mainnet, etc.).
# Pass an explicit network override as the second arg when needed (e.g. deploy to local with hoodi config).

# Fresh deployment (all contracts including proxies)
# Example: just deploy-fresh local
# just deploy-fresh hoodi-stage (deploys to hoodi)
# just deploy-fresh hoodi-stage local (deploys to local with hoodi-stage config)
deploy-fresh env="local" network="":
npx hardhat compile --force
npx tsx scripts/deploy-module.ts --network {{network}} --module {{module}} {{ if args == "" { "" } else { "--args '[\"" + replace(args, " ", "\",\"") + "\"]'" } }}
npx tsx scripts/deploy-fresh.ts --env {{env}} {{ if network == "" { "" } else { "--network " + network } }}

# Deploy an implementation contract (for proxy upgrades)
# Args: contract=<name> network=<name>
deploy-implementation contract network:
# Deploy implementations + modules (no proxy upgrade)
# Example: just deploy hoodi-prod
# just deploy mainnet
deploy env network="":
npx hardhat compile --force
npx tsx scripts/deploy-implementation.ts --network {{network}} --contract {{contract}}
npx tsx scripts/deploy.ts --env {{env}} {{ if network == "" { "" } else { "--network " + network } }}

# Deploy all contracts for a fresh deployment
# Args: network=<name>
deploy-all network:
# Upgrade on fork (pre-deployment validation)
upgrade-fork env="hoodi-stage":
npx hardhat compile --force
npx tsx scripts/deploy-all.ts --network {{network}}
npx tsx scripts/upgrade.ts --env {{env}} --fork --network local

# Update/replace a module in the proxy (hot-swap module)
# Args: module=<name> proxy=<address> network=<name> [args=init_args]
update-module module proxy network *args:
# Fork tests
test-fork env="hoodi-stage":
npx hardhat compile --force
npx tsx scripts/update-module.ts --network {{network}} --module {{module}} --proxy-address {{proxy}} {{ if args == "" { "" } else { "--args '[\"" + replace(args, " ", "\",\"") + "\"]'" } }}
npx tsx scripts/run-forked-tests.ts --env {{env}} --fork-network hardhat_forked --use-deployed-state true --strict-deployed-state true --allow-deployed-fallback false --no-gas-enforce true

# Upgrade a contract via UUPS proxy pattern
# Args: contract=<name> proxy=<address> network=<name>
upgrade-contract contract proxy network:
# End-to-end fork workflow: upgrade then run strict tests
upgrade-test-fork env="hoodi-stage":
just upgrade-fork {{env}}
just test-fork {{env}}

# Live upgrade (owner key required)
# Example: just upgrade hoodi-stage
# just upgrade hoodi-prod
upgrade env network="":
npx hardhat compile --force
npx tsx scripts/upgrade-contract.ts --network {{network}} --contract {{contract}} --proxy-address {{proxy}}
npx tsx scripts/upgrade.ts --env {{env}} {{ if network == "" { "" } else { "--network " + network } }}

# Generate SAFE multi-sig batch
generate-safe-batch env="mainnet":
npx tsx scripts/generate-safe-batch.ts --env {{env}}

# Verify on-chain state
verify-upgrade env:
npx tsx scripts/upgrade.ts --env {{env}} --verify-only

# Upgrade proxy to a specific implementation address
# Args: contract=<name> proxy=<address> implementation=<address> network=<name>
upgrade-implementation contract proxy implementation network:
npx tsx scripts/upgrade-with-impl.ts --network {{network}} --contract {{contract}} --proxy-address {{proxy}} --impl-address {{implementation}}
# === One-off Utilities ===

# Deploy a specific module contract (e.g., SSVOperators, SSVClusters)
deploy-module module network *args:
npx hardhat compile --force
npx tsx scripts/deploy-module.ts --network {{network}} --module {{module}} {{ if args == "" { "" } else { "--args '[\"" + replace(args, " ", "\",\"") + "\"]'" } }}

# Attach an existing deployed module to the proxy
# Args: module=<name> module-address=<address> proxy-address=<address> network=<name>
attach-module module module-address proxy-address network:
npx hardhat compile --force
npx tsx scripts/attach-module.ts --network {{network}} --module {{module}} --module-address {{module-address}} --proxy-address {{proxy-address}}

# Special upgrade task for SSVStaking module (handles CSSVToken integration)
# Args: proxy=<address> network=<name>
upgrade-ssv-staking proxy network:
# Upgrade a contract via UUPS proxy pattern (optionally with pre-deployed impl)
upgrade-contract contract proxy network *impl:
npx hardhat compile --force
npx tsx scripts/staking-upgrade.ts --network {{network}} --proxy-address {{proxy}}
npx tsx scripts/upgrade-contract.ts --network {{network}} --contract {{contract}} --proxy-address {{proxy}} {{ if impl == "" { "" } else { "--impl-address " + impl } }}

# Verify contract source code on Etherscan/block explorer
# Args: address=<contract_address> network=<name>
verify address network:
npx hardhat verify --network "{{network}}" "{{address}}"

# Export contract ABIs to JSON files for external use
abis:
npx hardhat compile --force
npx tsx scripts/common/export-abis.ts

# === Canonical Fork/Deploy Workflow ===
# Local fork defaults to anvil at http://127.0.0.1:8545.
# Override profile files with FORK_CONFIG_PATH / FORK_RESULT_PATH.

# Upgrade and configure on local fork (writes result to JSON)
# Uses FORK_NETWORK, FORK_CONFIG_PATH, FORK_RESULT_PATH env vars
upgrade-fork:
npx hardhat compile --force
npx tsx scripts/upgrade-fork.ts --network ${FORK_NETWORK:-local} --config ${FORK_CONFIG_PATH:-deployments/hoodi-upgrade.config.json} --output-config ${FORK_RESULT_PATH:-deployments/hoodi-upgrade.result.json}

# Run strict tests against deployed instances from fork result JSON (no fallback)
# Uses FORK_TEST_NETWORK, FORK_RESULT_PATH env vars
test-fork:
npx hardhat compile --force
npx tsx scripts/run-forked-local-tests.ts --fork-network ${FORK_TEST_NETWORK:-hardhat_forked} --config ${FORK_RESULT_PATH:-deployments/hoodi-upgrade.result.json} --use-deployed-state true --strict-deployed-state true --allow-deployed-fallback false --no-gas-enforce true

# End-to-end fork workflow: upgrade then run strict tests
upgrade-test-fork:
just upgrade-fork
just test-fork

# Execute live upgrade on Hoodi testnet (non-impersonating owner flow)
# Uses HOODI_CONFIG_PATH, HOODI_RESULT_PATH env vars
upgrade-hoodi:
npx hardhat compile --force
npx tsx scripts/upgrade-hoodi.ts --network hoodi --config ${HOODI_CONFIG_PATH:-deployments/hoodi-upgrade.config.json} --output-config ${HOODI_RESULT_PATH:-deployments/hoodi-upgrade.result.json}

# Mainnet deploy-only flow (modules + CSSVToken, no proxy upgrade)
# Uses MAINNET_DEPLOY_CONFIG_PATH, MAINNET_DEPLOY_RESULT_PATH env vars
deploy-mainnet:
npx hardhat compile --force
npx tsx scripts/deploy-mainnet.ts --network mainnet --config ${MAINNET_DEPLOY_CONFIG_PATH:-deployments/mainnet-upgrade.config.json} --output-config ${MAINNET_DEPLOY_RESULT_PATH:-deployments/mainnet-upgrade.result.json}
npx tsx scripts/common/export-abis.ts
2 changes: 1 addition & 1 deletion contracts/modules/SSVStaking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ contract SSVStaking is ISSVStaking, SSVReentrancyGuard {

uint64 private constant MINIMAL_STAKING_AMOUNT = 1_000_000_000;
uint64 private constant PRECISION = 1e18;
uint256 private constant MAX_PENDING_REQUESTS = 10;
uint256 private constant MAX_PENDING_REQUESTS = 2000;

address public immutable CSSV_ADDRESS;

Expand Down
3 changes: 2 additions & 1 deletion contracts/modules/SSVValidators.sol
Original file line number Diff line number Diff line change
Expand Up @@ -223,8 +223,9 @@ contract SSVValidators is ISSVValidators {
// When cluster becomes empty, clean up any remaining deviation
if (cluster.validatorCount == 0) {
uint64 remainingVUnits = ebSnapshot.vUnits;
if (remainingVUnits > 0) {
if (remainingVUnits > 0 && cluster.active) {
// remainingVUnits is pure deviation (no baseline left since validatorCount=0)
// Skip for liquidated clusters: deviation already cleaned up in _executeLiquidation
uint256 operatorsLength = operatorIds.length;
for (uint256 i; i < operatorsLength; ++i) {
seb.operatorEthVUnits[operatorIds[i]] -= remainingVUnits;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,19 @@ import {MAX_DELEGATION_SLOTS} from "../../../libraries/storage/SSVStorageStaking
contract SSVNetworkSSVStakingUpgrade is SSVNetwork {
function initializeSSVStaking(
uint64 cooldownDuration,
uint32[MAX_DELEGATION_SLOTS] memory defaultOracleIds
uint32[MAX_DELEGATION_SLOTS] memory defaultOracleIds,
uint16 quorumBps
) external onlyOwner reinitializer(3) {
if (quorumBps == 0 || quorumBps > 10_000) revert InvalidQuorum();

// save staking storage updates
StorageStaking storage s = SSVStorageStaking.load();
s.cooldownDuration = cooldownDuration;
s.defaultOracleIds = defaultOracleIds;
s.quorumBps = quorumBps;

emit CooldownDurationUpdated(cooldownDuration);
emit QuorumUpdated(quorumBps);
emit SSVNetworkUpgradeBlock("v2.0.0", block.number);
}
}
Loading
Loading