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
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,29 @@ abstract contract ValidatorRegistrator is Governable, Pausable {
EXIT_COMPLETE // validator has funds withdrawn to the EigenPod and is removed from the SSV
}

event RegistratorChanged(address newAddress);
event StakingMonitorChanged(address newAddress);
event ETHStaked(bytes pubkey, uint256 amount, bytes withdrawal_credentials);
event SSVValidatorRegistered(bytes pubkey, uint64[] operatorIds);
event SSVValidatorExitInitiated(bytes pubkey, uint64[] operatorIds);
event SSVValidatorExitCompleted(bytes pubkey, uint64[] operatorIds);
event RegistratorChanged(address indexed newAddress);
event StakingMonitorChanged(address indexed newAddress);
event ETHStaked(
bytes32 indexed pubKeyHash,
bytes pubKey,
uint256 amount,
bytes withdrawal_credentials
);
event SSVValidatorRegistered(
bytes32 indexed pubKeyHash,
bytes pubKey,
uint64[] operatorIds
);
event SSVValidatorExitInitiated(
bytes32 indexed pubKeyHash,
bytes pubKey,
uint64[] operatorIds
);
event SSVValidatorExitCompleted(
bytes32 indexed pubKeyHash,
bytes pubKey,
uint64[] operatorIds
);
event StakeETHThresholdChanged(uint256 amount);
event StakeETHTallyReset();

Expand Down Expand Up @@ -171,8 +188,8 @@ abstract contract ValidatorRegistrator is Governable, Pausable {
uint256 validatorsLength = validators.length;
// For each validator
for (uint256 i = 0; i < validatorsLength; ) {
bytes32 pubkeyHash = keccak256(validators[i].pubkey);
VALIDATOR_STATE currentState = validatorsStates[pubkeyHash];
bytes32 pubKeyHash = keccak256(validators[i].pubkey);
VALIDATOR_STATE currentState = validatorsStates[pubKeyHash];

require(
currentState == VALIDATOR_STATE.REGISTERED,
Expand All @@ -189,12 +206,13 @@ abstract contract ValidatorRegistrator is Governable, Pausable {
);

emit ETHStaked(
pubKeyHash,
validators[i].pubkey,
32 ether,
withdrawal_credentials
);

validatorsStates[pubkeyHash] = VALIDATOR_STATE.STAKED;
validatorsStates[pubKeyHash] = VALIDATOR_STATE.STAKED;

unchecked {
++i;
Expand All @@ -216,9 +234,9 @@ abstract contract ValidatorRegistrator is Governable, Pausable {
uint256 amount,
Cluster calldata cluster
) external onlyRegistrator whenNotPaused {
bytes32 pubKeyHash = keccak256(publicKey);
require(
validatorsStates[keccak256(publicKey)] ==
VALIDATOR_STATE.NON_REGISTERED,
validatorsStates[pubKeyHash] == VALIDATOR_STATE.NON_REGISTERED,
"Validator already registered"
);
ISSVNetwork(SSV_NETWORK_ADDRESS).registerValidator(
Expand All @@ -228,8 +246,9 @@ abstract contract ValidatorRegistrator is Governable, Pausable {
amount,
cluster
);
validatorsStates[keccak256(publicKey)] = VALIDATOR_STATE.REGISTERED;
emit SSVValidatorRegistered(publicKey, operatorIds);
emit SSVValidatorRegistered(pubKeyHash, publicKey, operatorIds);

validatorsStates[pubKeyHash] = VALIDATOR_STATE.REGISTERED;
}

// slither-disable-end reentrancy-no-eth
Expand All @@ -242,13 +261,14 @@ abstract contract ValidatorRegistrator is Governable, Pausable {
bytes calldata publicKey,
uint64[] calldata operatorIds
) external onlyRegistrator whenNotPaused {
VALIDATOR_STATE currentState = validatorsStates[keccak256(publicKey)];
bytes32 pubKeyHash = keccak256(publicKey);
VALIDATOR_STATE currentState = validatorsStates[pubKeyHash];
require(currentState == VALIDATOR_STATE.STAKED, "Validator not staked");

ISSVNetwork(SSV_NETWORK_ADDRESS).exitValidator(publicKey, operatorIds);
emit SSVValidatorExitInitiated(publicKey, operatorIds);
emit SSVValidatorExitInitiated(pubKeyHash, publicKey, operatorIds);

validatorsStates[keccak256(publicKey)] = VALIDATOR_STATE.EXITING;
validatorsStates[pubKeyHash] = VALIDATOR_STATE.EXITING;
}

// slither-disable-end reentrancy-no-eth
Expand All @@ -263,7 +283,8 @@ abstract contract ValidatorRegistrator is Governable, Pausable {
uint64[] calldata operatorIds,
Cluster calldata cluster
) external onlyRegistrator whenNotPaused {
VALIDATOR_STATE currentState = validatorsStates[keccak256(publicKey)];
bytes32 pubKeyHash = keccak256(publicKey);
VALIDATOR_STATE currentState = validatorsStates[pubKeyHash];
require(
currentState == VALIDATOR_STATE.EXITING,
"Validator not exiting"
Expand All @@ -274,9 +295,9 @@ abstract contract ValidatorRegistrator is Governable, Pausable {
operatorIds,
cluster
);
emit SSVValidatorExitCompleted(publicKey, operatorIds);
emit SSVValidatorExitCompleted(pubKeyHash, publicKey, operatorIds);

validatorsStates[keccak256(publicKey)] = VALIDATOR_STATE.EXIT_COMPLETE;
validatorsStates[pubKeyHash] = VALIDATOR_STATE.EXIT_COMPLETE;
}

// slither-disable-end reentrancy-no-eth
Expand Down
21 changes: 17 additions & 4 deletions contracts/test/behaviour/ssvStrategy.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,11 @@ const shouldBehaveLikeAnSsvStrategy = (context) => {
);
await expect(regTx)
.to.emit(nativeStakingSSVStrategy, "SSVValidatorRegistered")
.withArgs(testValidator.publicKey, testValidator.operatorIds);
.withArgs(
keccak256(testValidator.publicKey),
testValidator.publicKey,
testValidator.operatorIds
);

expect(
await nativeStakingSSVStrategy.validatorsStates(
Expand All @@ -221,7 +225,8 @@ const shouldBehaveLikeAnSsvStrategy = (context) => {
await expect(stakeTx)
.to.emit(nativeStakingSSVStrategy, "ETHStaked")
.withNamedArgs({
pubkey: testValidator.publicKey,
pubKeyHash: keccak256(testValidator.publicKey),
pubKey: testValidator.publicKey,
amount: oethUnits("32"),
});

Expand Down Expand Up @@ -397,7 +402,11 @@ const shouldBehaveLikeAnSsvStrategy = (context) => {

await expect(exitTx)
.to.emit(nativeStakingSSVStrategy, "SSVValidatorExitInitiated")
.withArgs(testValidator.publicKey, testValidator.operatorIds);
.withArgs(
keccak256(testValidator.publicKey),
testValidator.publicKey,
testValidator.operatorIds
);

const removeTx = await nativeStakingSSVStrategy
.connect(validatorRegistrator)
Expand All @@ -409,7 +418,11 @@ const shouldBehaveLikeAnSsvStrategy = (context) => {

await expect(removeTx)
.to.emit(nativeStakingSSVStrategy, "SSVValidatorExitCompleted")
.withArgs(testValidator.publicKey, testValidator.operatorIds);
.withArgs(
keccak256(testValidator.publicKey),
testValidator.publicKey,
testValidator.operatorIds
);
});
});

Expand Down
26 changes: 22 additions & 4 deletions contracts/test/strategies/nativeSSVStaking.js
Original file line number Diff line number Diff line change
Expand Up @@ -1090,7 +1090,7 @@ describe("Unit test: Native SSV Staking Strategy", function () {

const stakeAmount = ethUnits("32");
// Register a new validator with the SSV Network
await nativeStakingSSVStrategy
const regTx = await nativeStakingSSVStrategy
.connect(validatorRegistrator)
.registerSsvValidator(
testPublicKeys[i],
Expand All @@ -1100,14 +1100,22 @@ describe("Unit test: Native SSV Staking Strategy", function () {
emptyCluster
);

await expect(regTx)
.to.emit(nativeStakingSSVStrategy, "SSVValidatorRegistered")
.withArgs(
keccak256(testPublicKeys[i]),
testPublicKeys[i],
testValidator.operatorIds
);

expect(
await nativeStakingSSVStrategy.validatorsStates(
keccak256(testPublicKeys[i])
)
).to.equal(1, "Validator state not 1 (REGISTERED)");

// Stake ETH to the new validator
const tx = nativeStakingSSVStrategy
const stakeTx = nativeStakingSSVStrategy
.connect(validatorRegistrator)
.stakeEth([
{
Expand All @@ -1118,9 +1126,19 @@ describe("Unit test: Native SSV Staking Strategy", function () {
]);

if (stakeTresholdErrorTriggered && i == validators - 1) {
await expect(tx).to.be.revertedWith("Staking ETH over threshold");
await expect(stakeTx).to.be.revertedWith(
"Staking ETH over threshold"
);
} else {
await tx;
await stakeTx;

await expect(stakeTx)
.to.emit(nativeStakingSSVStrategy, "ETHStaked")
.withNamedArgs({
pubKeyHash: keccak256(testPublicKeys[i]),
pubKey: testPublicKeys[i],
amount: parseEther("32"),
});

expect(
await nativeStakingSSVStrategy.validatorsStates(
Expand Down