Skip to content
Open
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
4 changes: 4 additions & 0 deletions test/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export const PRECISION_FACTOR = 10000n;
export const MINIMAL_LIQUIDATION_THRESHOLD = 21480n;
export const STAKE_AMOUNT = ethers.parseEther("10");
export const DEFAULT_ORACLES_IDS = envBigIntArray("FORK_DEFAULT_ORACLE_IDS", [1n, 2n, 3n, 4n]);
export const QUORUM_BPS = envBigInt("FORK_QUORUM_BPS", 7500n);
export const DEFAULT_UNSTAKE_COOLDOWN = envBigInt(
"FORK_DEFAULT_UNSTAKE_COOLDOWN",
7n * 24n * 60n * 60n
Expand All @@ -51,3 +52,6 @@ export const DEDUCTED_DIGITS = 10_000_000n;
export const ETH_DEDUCTED_DIGITS = 100_000n;
export const OPERATOR_FEE_PRECISION = ETH_DEDUCTED_DIGITS;
export const BPS_DENOMINATOR = PRECISION_FACTOR;
export const TOKEN_REGISTER_AMOUNT = ethers.parseEther("100");
export const MINIMAL_OPERATOR_FEE_SSV = 1000000000n;
export const DEFAULT_OPERATOR_ETH_FEE = 1770_000_000n;
139 changes: 139 additions & 0 deletions test/common/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,22 @@ import {
MINIMAL_OPERATOR_ETH_FEE,
SSV_MODULE_CONTRACTS,
VUNITS_PRECISION,
MINIMAL_OPERATOR_FEE_SSV,
DEFAULT_UNSTAKE_COOLDOWN,
DEFAULT_ORACLES_IDS,
QUORUM_BPS,
NETWORK_FEE,
NETWORK_FEE_ETH,
MINIMUM_LIQUIDATION_PERIOD_COLLATERAL,
MINIMUM_BLOCKS_BEFORE_LIQUIDATION,
MAXIMUM_OPERATORS_FEE,
OPERATOR_MAX_FEE_INCREASE,
} from './constants.ts';
import type { NetworkConnection } from 'hardhat/types/network';
import type { Cluster, ClusterTuple, OperatorTuple, SSVModules } from './types.ts';
import type { SSVNetwork, SSVNetworkViews } from '../../types/ethers-contracts/index.js';
import type { HardhatEthersSigner } from '@nomicfoundation/hardhat-ethers/types';
import { attachModule, deployContract, getDeployer, upgradeProxy } from '../../scripts/common/helpers.js';

export function makePublicKey(seed: number): string {
return `0x${seed.toString(16).padStart(96, "0")}`;
Expand Down Expand Up @@ -80,6 +91,26 @@ export async function registerOperators(network: any, owner: any, count: number)
return operatorIds;
}

export async function registerOperatorsSSV(network: any, owner: any, count: number): Promise<number[]> {
const operatorIds: number[] = [];

for (let i = 0; i < count; i += 1) {
const expectedId = await network.connect(owner).registerOperator.staticCall(
makeOperatorKey(i + 1), MINIMAL_OPERATOR_FEE_SSV, true
);

const tx = await network
.connect(owner)
.registerOperator(
makeOperatorKey(i + 1), MINIMAL_OPERATOR_FEE_SSV, true
);
await tx.wait();
operatorIds.push(expectedId);
}

return operatorIds;
}

type OperatorFeeViews = Pick<
SSVNetworkViews,
"getOperatorFee" | "getMaximumOperatorFee" | "getOperatorFeeIncreaseLimit"
Expand Down Expand Up @@ -236,6 +267,7 @@ const EVENT_ABI = [
'event ClusterReactivated(address indexed owner, uint64[] operatorIds, tuple(uint32, uint64, uint64, bool, uint256) cluster)',
'event ValidatorAdded(address indexed owner, uint64[] operatorIds, bytes publicKey, bytes shares, tuple(uint32, uint64, uint64, bool, uint256) cluster)',
'event ValidatorRemoved(address indexed owner, uint64[] operatorIds, bytes publicKey, tuple(uint32, uint64, uint64, bool, uint256) cluster)',
'event ClusterMigratedToETH(address indexed owner, uint64[] operatorIds, uint256 ethDeposited, uint256 ssvRefunded, uint32 effectiveBalance, tuple(uint32, uint64, uint64, bool, uint256) cluster)',
] as const;

export function parseClusterFromEvent(contract: any, receipt: any, eventName: string): Cluster {
Expand Down Expand Up @@ -572,3 +604,110 @@ export async function updateClusterBalancesForDefaultClusters(
await tx.wait();
}
}

export async function upgradeToStakingVersion(
connection: any,
network: any,
views: any,
): Promise<{
cssv: any;
newNetwork: SSVNetwork;
newViews: SSVNetworkViews;
}> {
const deployer = await getDeployer(connection.ethers);
const networkAddress = await network.getAddress();

const { contract: cssv, address: cssvTokenAddress } =
await deployContract(connection.ethers, "CSSVToken", [networkAddress]);

const latestBlock = await connection.ethers.provider.getBlock("latest");
const upgradeBlockNum = latestBlock.number;

const { address: upgradeImplAddr } =
await deployContract(connection.ethers, "SSVNetworkSSVStakingUpgrade");

await upgradeProxy(
connection.ethers,
deployer,
networkAddress,
upgradeImplAddr,
"SSVNetworkSSVStakingUpgrade",
"initializeSSVStaking(uint64,uint32[4],uint16)",
[DEFAULT_UNSTAKE_COOLDOWN, DEFAULT_ORACLES_IDS, QUORUM_BPS]
);

const networkFactory =
await connection.ethers.getContractFactory("SSVNetwork");
const upgradedNetwork = networkFactory.attach(networkAddress);

const moduleNames = [
"SSVClusters",
"SSVOperatorsWhitelist",
"SSVValidators",
];
const moduleAddresses: Record<string, string> = {};

const { address: ssvOperatorsAddr } =
await deployContract(connection.ethers, "SSVOperators", [upgradeBlockNum]);
moduleAddresses["SSVOperators"] = ssvOperatorsAddr;

const { address: ssvDaoAddr } =
await deployContract(connection.ethers, "SSVDAO", [cssvTokenAddress]);
moduleAddresses["SSVDAO"] = ssvDaoAddr;

const { address: ssvViewsAddr } =
await deployContract(connection.ethers, "SSVViews", [cssvTokenAddress]);
moduleAddresses["SSVViews"] = ssvViewsAddr;

const { address: ssvStakingAddr } =
await deployContract(connection.ethers, "SSVStaking", [cssvTokenAddress]);
moduleAddresses["SSVStaking"] = ssvStakingAddr;

for (const mod of moduleNames) {
const { address } = await deployContract(connection.ethers, mod);
moduleAddresses[mod] = address;
}

for (const [name, addr] of Object.entries(moduleAddresses)) {
await attachModule(connection.ethers, networkAddress, name, addr);
}

const { address: newViewsImpl } =
await deployContract(connection.ethers, "SSVNetworkViews");

await views.upgradeTo(newViewsImpl);

const viewsFactory =
await connection.ethers.getContractFactory("SSVNetworkViews");
const upgradedViews = viewsFactory.attach(await views.getAddress());

await (await upgradedNetwork.updateNetworkFeeSSV(NETWORK_FEE)).wait();
await (await upgradedNetwork.updateNetworkFee(NETWORK_FEE_ETH)).wait();
await (await upgradedNetwork.updateMinimumLiquidationCollateral(
MINIMUM_LIQUIDATION_PERIOD_COLLATERAL
)).wait();
await (await upgradedNetwork.updateMinimumLiquidationCollateralSSV(
MINIMUM_LIQUIDATION_PERIOD_COLLATERAL
)).wait();
await (await upgradedNetwork.updateLiquidationThresholdPeriod(
MINIMUM_BLOCKS_BEFORE_LIQUIDATION
)).wait();
await (await upgradedNetwork.updateLiquidationThresholdPeriodSSV(
MINIMUM_BLOCKS_BEFORE_LIQUIDATION
)).wait();
await (await upgradedNetwork.updateMaximumOperatorFee(
MAXIMUM_OPERATORS_FEE
)).wait();
await (await upgradedNetwork.updateOperatorFeeIncreaseLimit(
OPERATOR_MAX_FEE_INCREASE
)).wait();
await (await upgradedNetwork.updateMinimumOperatorEthFee(
MINIMAL_OPERATOR_ETH_FEE
)).wait();

return {
cssv,
newNetwork: upgradedNetwork,
newViews: upgradedViews,
};
}
9 changes: 9 additions & 0 deletions test/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ export interface Operator {
isActive: boolean;
}

export interface OperatorSSV {
owner: string;
fee: bigint;
validatorCount: bigint;
whitelistedAddress: string;
isPrivate: boolean;
isActive: boolean;
}

export interface UnstakeRequest {
amount: bigint;
unlockTime: bigint;
Expand Down
Loading
Loading