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
5 changes: 0 additions & 5 deletions contracts-abi/abi/VanillaRegistry.abi
Original file line number Diff line number Diff line change
Expand Up @@ -1208,11 +1208,6 @@
"name": "InvalidReceive",
"inputs": []
},
{
"type": "error",
"name": "MinStakeMustBePositive",
"inputs": []
},
{
"type": "error",
"name": "MustUnstakeToWithdraw",
Expand Down
2 changes: 1 addition & 1 deletion contracts-abi/clients/VanillaRegistry/VanillaRegistry.go

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion contracts/contracts/interfaces/IVanillaRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ interface IVanillaRegistry {
error WithdrawalFailed();
error NoFundsToWithdraw();
error SlashingTransferFailed();
error MinStakeMustBePositive();
error SlashAmountMustBePositive();
error SlashAmountMustBeLessThanMinStake();
error SlashOracleMustBeSet();
Expand Down
5 changes: 3 additions & 2 deletions contracts/contracts/validator-registry/VanillaRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,6 @@ contract VanillaRegistry is IVanillaRegistry, VanillaRegistryStorage,

/// @dev Internal function to set the minimum stake parameter.
function _setMinStake(uint256 newMinStake) internal {
require(newMinStake != 0, IVanillaRegistry.MinStakeMustBePositive());
minStake = newMinStake;
emit MinStakeSet(msg.sender, newMinStake);
}
Expand Down Expand Up @@ -427,7 +426,9 @@ contract VanillaRegistry is IVanillaRegistry, VanillaRegistryStorage,

/// @dev Internal function to check if a validator is considered "opted-in" to mev-commit via this registry.
function _isValidatorOptedIn(bytes calldata valBLSPubKey) internal view returns (bool) {
return !_isUnstaking(valBLSPubKey) && stakedValidators[valBLSPubKey].balance >= minStake;
return !_isUnstaking(valBLSPubKey) &&
stakedValidators[valBLSPubKey].balance >= minStake &&
whitelistedStakers[stakedValidators[valBLSPubKey].withdrawalAddress];
}

/// @dev Internal function to check if a validator is currently unstaking.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// SPDX-License-Identifier: BSL 1.1

// solhint-disable no-console
// solhint-disable one-contract-per-file

pragma solidity 0.8.26;

import {Script} from "forge-std/Script.sol";
import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol";
import {VanillaRegistry} from "../../contracts/validator-registry/VanillaRegistry.sol";
import {console} from "forge-std/console.sol";
import {MainnetConstants} from "../MainnetConstants.sol";

contract BaseDeploy is Script {
function deployVanillaRegistry(
uint256 minStake,
address slashOracle,
address slashReceiver,
uint256 unstakePeriodBlocks,
uint256 payoutPeriodBlocks,
address owner
) public returns (address) {
console.log("Deploying VanillaReputationalRegistry on chain:", block.chainid);
address proxy = Upgrades.deployUUPSProxy(
"VanillaRegistry.sol",
abi.encodeCall(
VanillaRegistry.initialize,
(minStake, slashOracle, slashReceiver, unstakePeriodBlocks, payoutPeriodBlocks, owner)
)
);
console.log("VanillaReputationalRegistry UUPS proxy deployed to:", address(proxy));
VanillaRegistry vanillaRegistry = VanillaRegistry(payable(proxy));
console.log("VanillaReputationalRegistry owner:", vanillaRegistry.owner());
return proxy;
}
}

contract DeployMainnet is BaseDeploy {
address constant public OWNER = MainnetConstants.PRIMEV_TEAM_MULTISIG;
uint256 constant public MIN_STAKE = 0 ether;
address constant public SLASH_ORACLE = MainnetConstants.PRIMEV_TEAM_MULTISIG;
address constant public SLASH_RECEIVER = MainnetConstants.COMMITMENT_HOLDINGS_MULTISIG;
uint256 constant public UNSTAKE_PERIOD_BLOCKS = 7200; // 7200 * 12s ~= 1 day.
uint256 constant public PAYOUT_PERIOD_BLOCKS = 12000; // ~ 1 day

function run() external {
require(block.chainid == 1, "must deploy on mainnet");
vm.startBroadcast();
deployVanillaRegistry(MIN_STAKE, SLASH_ORACLE, SLASH_RECEIVER, UNSTAKE_PERIOD_BLOCKS, PAYOUT_PERIOD_BLOCKS, OWNER);
vm.stopBroadcast();
}
}


contract DeployHoodi is BaseDeploy {
uint256 constant public MIN_STAKE = 0 ether; // 10k vals = 1 ETH cost
address constant public SLASH_ORACLE = 0x1623fE21185c92BB43bD83741E226288B516134a;
address constant public SLASH_RECEIVER = 0x1623fE21185c92BB43bD83741E226288B516134a;
uint256 constant public UNSTAKE_PERIOD_BLOCKS = 32 * 3; // 2 epoch finalization time + settlement buffer
uint256 constant public PAYOUT_PERIOD = 10000; // 10k * 12s = 1.39 days

// This is the most important field. On mainnet it'll be the primev multisig.
address constant public OWNER = 0x1623fE21185c92BB43bD83741E226288B516134a;

function run() external {
require(block.chainid == 560048, "must deploy on Hoodi");
vm.startBroadcast();
deployVanillaRegistry(MIN_STAKE, SLASH_ORACLE, SLASH_RECEIVER, UNSTAKE_PERIOD_BLOCKS, PAYOUT_PERIOD, OWNER);
vm.stopBroadcast();
}
}
72 changes: 71 additions & 1 deletion contracts/test/validator-registry/VanillaRegistryTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,10 @@ contract VanillaRegistryTest is Test {
vm.expectEmit(true, true, true, true);
emit Staked(owner, user1, user2BLSKey, MIN_STAKE);
validatorRegistry.delegateStake{value: 2*MIN_STAKE}(validators, user1); // Both validators are opted-in on user1's behalf

address[] memory stakers = new address[](2);
stakers[0] = user1;
stakers[1] = user2;
validatorRegistry.whitelistStakers(stakers);
vm.stopPrank();

assertEq(address(owner).balance, 7 ether);
Expand Down Expand Up @@ -1184,4 +1187,71 @@ contract VanillaRegistryTest is Test {
vm.expectRevert(abi.encodeWithSelector(IVanillaRegistry.StakerNotWhitelisted.selector, user1));
validatorRegistry.removeWhitelistedStakers(stakers);
}

function testMinStakeZeroRegisterUnregister() public {
// Allow minStake = 0
vm.prank(owner);
validatorRegistry.setMinStake(0);
assertEq(validatorRegistry.minStake(), 0);

// Whitelist user1 (if already whitelisted in other flows, this test runs in isolation)
vm.prank(owner);
address[] memory stakers = new address[](1);
stakers[0] = user1;
validatorRegistry.whitelistStakers(stakers);

// Stake normally; with minStake=0 any positive amount should work the same
vm.deal(user1, MIN_STAKE);
vm.prank(user1);
bytes[] memory validators = new bytes[](1);
validators[0] = user1BLSKey;
validatorRegistry.stake{value: MIN_STAKE}(validators);

assertEq(validatorRegistry.getStakedAmount(user1BLSKey), MIN_STAKE);
assertTrue(validatorRegistry.isValidatorOptedIn(user1BLSKey));

// Unstake then withdraw; should work with minStake=0
vm.prank(user1);
validatorRegistry.unstake(validators);

vm.roll(block.number + UNSTAKE_PERIOD + 1);

vm.prank(user1);
validatorRegistry.withdraw(validators);

assertEq(validatorRegistry.getStakedAmount(user1BLSKey), 0);
assertFalse(validatorRegistry.isValidatorOptedIn(user1BLSKey));
}

function testIsValidatorOptedInRespectsWhitelist() public {
// Whitelist and stake
vm.prank(owner);
address[] memory stakers = new address[](1);
stakers[0] = user1;
validatorRegistry.whitelistStakers(stakers);

vm.deal(user1, MIN_STAKE);
vm.prank(user1);
bytes[] memory validators = new bytes[](1);
validators[0] = user1BLSKey;
validatorRegistry.stake{value: MIN_STAKE}(validators);

// With whitelist + balance >= minStake and not unstaking -> true
assertTrue(validatorRegistry.isValidatorOptedIn(user1BLSKey));

// Remove from whitelist -> should be false even with balance
vm.prank(owner);
validatorRegistry.removeWhitelistedStakers(stakers);
assertFalse(validatorRegistry.isValidatorOptedIn(user1BLSKey));

// Re-whitelist -> true again
vm.prank(owner);
validatorRegistry.whitelistStakers(stakers);
assertTrue(validatorRegistry.isValidatorOptedIn(user1BLSKey));

// Start unstaking -> false due to _isUnstaking()
vm.prank(user1);
validatorRegistry.unstake(validators);
assertFalse(validatorRegistry.isValidatorOptedIn(user1BLSKey));
}
}
Loading