diff --git a/contracts-abi/abi/VanillaRegistry.abi b/contracts-abi/abi/VanillaRegistry.abi index 208370b00..ef690d870 100644 --- a/contracts-abi/abi/VanillaRegistry.abi +++ b/contracts-abi/abi/VanillaRegistry.abi @@ -379,6 +379,19 @@ ], "stateMutability": "view" }, + { + "type": "function", + "name": "removeWhitelistedStakers", + "inputs": [ + { + "name": "stakers", + "type": "address[]", + "internalType": "address[]" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, { "type": "function", "name": "renounceOwnership", @@ -633,6 +646,38 @@ "outputs": [], "stateMutability": "payable" }, + { + "type": "function", + "name": "whitelistStakers", + "inputs": [ + { + "name": "stakers", + "type": "address[]", + "internalType": "address[]" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "whitelistedStakers", + "inputs": [ + { + "name": "staker", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "whitelisted", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "withdraw", @@ -941,6 +986,44 @@ ], "anonymous": false }, + { + "type": "event", + "name": "StakerRemovedFromWhitelist", + "inputs": [ + { + "name": "msgSender", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "staker", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "StakerWhitelisted", + "inputs": [ + { + "name": "msgSender", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "staker", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, { "type": "event", "name": "TotalStakeWithdrawn", @@ -1188,6 +1271,17 @@ } ] }, + { + "type": "error", + "name": "SenderIsNotWhitelistedStaker", + "inputs": [ + { + "name": "sender", + "type": "address", + "internalType": "address" + } + ] + }, { "type": "error", "name": "SenderIsNotWithdrawalAddress", @@ -1250,6 +1344,28 @@ } ] }, + { + "type": "error", + "name": "StakerAlreadyWhitelisted", + "inputs": [ + { + "name": "staker", + "type": "address", + "internalType": "address" + } + ] + }, + { + "type": "error", + "name": "StakerNotWhitelisted", + "inputs": [ + { + "name": "staker", + "type": "address", + "internalType": "address" + } + ] + }, { "type": "error", "name": "TransferToRecipientFailed", diff --git a/contracts-abi/clients/VanillaRegistry/VanillaRegistry.go b/contracts-abi/clients/VanillaRegistry/VanillaRegistry.go index 677f6d9b3..117ccf2f0 100644 --- a/contracts-abi/clients/VanillaRegistry/VanillaRegistry.go +++ b/contracts-abi/clients/VanillaRegistry/VanillaRegistry.go @@ -45,7 +45,7 @@ type IVanillaRegistryStakedValidator struct { // VanillaregistryMetaData contains all meta data concerning the Vanillaregistry contract. var VanillaregistryMetaData = &bind.MetaData{ - ABI: "[{\"type\":\"constructor\",\"inputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"fallback\",\"stateMutability\":\"payable\"},{\"type\":\"receive\",\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"UPGRADE_INTERFACE_VERSION\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"string\",\"internalType\":\"string\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"acceptOwnership\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"addStake\",\"inputs\":[{\"name\":\"blsPubKeys\",\"type\":\"bytes[]\",\"internalType\":\"bytes[]\"}],\"outputs\":[],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"claimForceWithdrawnFunds\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"delegateStake\",\"inputs\":[{\"name\":\"blsPubKeys\",\"type\":\"bytes[]\",\"internalType\":\"bytes[]\"},{\"name\":\"withdrawalAddress\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"forceWithdrawalAsOwner\",\"inputs\":[{\"name\":\"blsPubKeys\",\"type\":\"bytes[]\",\"internalType\":\"bytes[]\"},{\"name\":\"withdrawalAddress\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"forceWithdrawnFunds\",\"inputs\":[{\"name\":\"withdrawalAddress\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"amountToClaim\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getAccumulatedSlashingFunds\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getBlocksTillWithdrawAllowed\",\"inputs\":[{\"name\":\"valBLSPubKey\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getStakedAmount\",\"inputs\":[{\"name\":\"valBLSPubKey\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getStakedValidator\",\"inputs\":[{\"name\":\"valBLSPubKey\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[{\"name\":\"\",\"type\":\"tuple\",\"internalType\":\"structIVanillaRegistry.StakedValidator\",\"components\":[{\"name\":\"exists\",\"type\":\"bool\",\"internalType\":\"bool\"},{\"name\":\"withdrawalAddress\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"balance\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"unstakeOccurrence\",\"type\":\"tuple\",\"internalType\":\"structBlockHeightOccurrence.Occurrence\",\"components\":[{\"name\":\"exists\",\"type\":\"bool\",\"internalType\":\"bool\"},{\"name\":\"blockHeight\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]}]}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"initialize\",\"inputs\":[{\"name\":\"_minStake\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_slashOracle\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_slashReceiver\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_unstakePeriodBlocks\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_slashingPayoutPeriodBlocks\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_owner\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"isSlashingPayoutDue\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"isUnstaking\",\"inputs\":[{\"name\":\"valBLSPubKey\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"isValidatorOptedIn\",\"inputs\":[{\"name\":\"valBLSPubKey\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"manuallyTransferSlashingFunds\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"minStake\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"owner\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"pause\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"paused\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"pendingOwner\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"proxiableUUID\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"renounceOwnership\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setMinStake\",\"inputs\":[{\"name\":\"newMinStake\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setSlashOracle\",\"inputs\":[{\"name\":\"newSlashOracle\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setSlashReceiver\",\"inputs\":[{\"name\":\"newSlashReceiver\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setSlashingPayoutPeriodBlocks\",\"inputs\":[{\"name\":\"newSlashingPayoutPeriodBlocks\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setUnstakePeriodBlocks\",\"inputs\":[{\"name\":\"newUnstakePeriodBlocks\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"slash\",\"inputs\":[{\"name\":\"blsPubKeys\",\"type\":\"bytes[]\",\"internalType\":\"bytes[]\"},{\"name\":\"payoutIfDue\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"slashOracle\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"slashingFundsTracker\",\"inputs\":[],\"outputs\":[{\"name\":\"recipient\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"accumulatedAmount\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"lastPayoutBlock\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"payoutPeriodBlocks\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"stake\",\"inputs\":[{\"name\":\"blsPubKeys\",\"type\":\"bytes[]\",\"internalType\":\"bytes[]\"}],\"outputs\":[],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"stakedValidators\",\"inputs\":[{\"name\":\"\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[{\"name\":\"exists\",\"type\":\"bool\",\"internalType\":\"bool\"},{\"name\":\"withdrawalAddress\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"balance\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"unstakeOccurrence\",\"type\":\"tuple\",\"internalType\":\"structBlockHeightOccurrence.Occurrence\",\"components\":[{\"name\":\"exists\",\"type\":\"bool\",\"internalType\":\"bool\"},{\"name\":\"blockHeight\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"transferOwnership\",\"inputs\":[{\"name\":\"newOwner\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"unpause\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"unstake\",\"inputs\":[{\"name\":\"blsPubKeys\",\"type\":\"bytes[]\",\"internalType\":\"bytes[]\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"unstakePeriodBlocks\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"upgradeToAndCall\",\"inputs\":[{\"name\":\"newImplementation\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"withdraw\",\"inputs\":[{\"name\":\"blsPubKeys\",\"type\":\"bytes[]\",\"internalType\":\"bytes[]\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"event\",\"name\":\"FeeTransfer\",\"inputs\":[{\"name\":\"amount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"},{\"name\":\"recipient\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Initialized\",\"inputs\":[{\"name\":\"version\",\"type\":\"uint64\",\"indexed\":false,\"internalType\":\"uint64\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"MinStakeSet\",\"inputs\":[{\"name\":\"msgSender\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newMinStake\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"OwnershipTransferStarted\",\"inputs\":[{\"name\":\"previousOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"OwnershipTransferred\",\"inputs\":[{\"name\":\"previousOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Paused\",\"inputs\":[{\"name\":\"account\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"SlashOracleSet\",\"inputs\":[{\"name\":\"msgSender\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newSlashOracle\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"SlashReceiverSet\",\"inputs\":[{\"name\":\"msgSender\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newSlashReceiver\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Slashed\",\"inputs\":[{\"name\":\"msgSender\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"slashReceiver\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"withdrawalAddress\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"valBLSPubKey\",\"type\":\"bytes\",\"indexed\":false,\"internalType\":\"bytes\"},{\"name\":\"amount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"SlashingPayoutPeriodBlocksSet\",\"inputs\":[{\"name\":\"msgSender\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newSlashingPayoutPeriodBlocks\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"StakeAdded\",\"inputs\":[{\"name\":\"msgSender\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"withdrawalAddress\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"valBLSPubKey\",\"type\":\"bytes\",\"indexed\":false,\"internalType\":\"bytes\"},{\"name\":\"amount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"},{\"name\":\"newBalance\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"StakeWithdrawn\",\"inputs\":[{\"name\":\"msgSender\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"withdrawalAddress\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"valBLSPubKey\",\"type\":\"bytes\",\"indexed\":false,\"internalType\":\"bytes\"},{\"name\":\"amount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Staked\",\"inputs\":[{\"name\":\"msgSender\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"withdrawalAddress\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"valBLSPubKey\",\"type\":\"bytes\",\"indexed\":false,\"internalType\":\"bytes\"},{\"name\":\"amount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"TotalStakeWithdrawn\",\"inputs\":[{\"name\":\"msgSender\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"withdrawalAddress\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"totalAmount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Unpaused\",\"inputs\":[{\"name\":\"account\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"UnstakePeriodBlocksSet\",\"inputs\":[{\"name\":\"msgSender\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newUnstakePeriodBlocks\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Unstaked\",\"inputs\":[{\"name\":\"msgSender\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"withdrawalAddress\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"valBLSPubKey\",\"type\":\"bytes\",\"indexed\":false,\"internalType\":\"bytes\"},{\"name\":\"amount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Upgraded\",\"inputs\":[{\"name\":\"implementation\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"error\",\"name\":\"AddressEmptyCode\",\"inputs\":[{\"name\":\"target\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"AtLeastOneRecipientRequired\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ERC1967InvalidImplementation\",\"inputs\":[{\"name\":\"implementation\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"ERC1967NonPayable\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"EnforcedPause\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ExpectedPause\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"FailedInnerCall\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"FeeRecipientIsZero\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidBLSPubKeyLength\",\"inputs\":[{\"name\":\"expected\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"actual\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"type\":\"error\",\"name\":\"InvalidFallback\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidInitialization\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidReceive\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"MinStakeMustBePositive\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"MustUnstakeToWithdraw\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"NoFundsToWithdraw\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"NotInitializing\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"OwnableInvalidOwner\",\"inputs\":[{\"name\":\"owner\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"OwnableUnauthorizedAccount\",\"inputs\":[{\"name\":\"account\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"PayoutPeriodMustBePositive\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"SenderIsNotSlashOracle\",\"inputs\":[{\"name\":\"sender\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"slashOracle\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"SenderIsNotWithdrawalAddress\",\"inputs\":[{\"name\":\"sender\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"withdrawalAddress\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"SlashAmountMustBeLessThanMinStake\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"SlashAmountMustBePositive\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"SlashOracleMustBeSet\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"SlashReceiverMustBeSet\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"SlashingPayoutPeriodMustBePositive\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"SlashingTransferFailed\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"StakeTooLowForNumberOfKeys\",\"inputs\":[{\"name\":\"msgValue\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"required\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"type\":\"error\",\"name\":\"TransferToRecipientFailed\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"UUPSUnauthorizedCallContext\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"UUPSUnsupportedProxiableUUID\",\"inputs\":[{\"name\":\"slot\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]},{\"type\":\"error\",\"name\":\"UnstakePeriodMustBePositive\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ValidatorCannotBeUnstaking\",\"inputs\":[{\"name\":\"valBLSPubKey\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]},{\"type\":\"error\",\"name\":\"ValidatorRecordMustExist\",\"inputs\":[{\"name\":\"valBLSPubKey\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]},{\"type\":\"error\",\"name\":\"ValidatorRecordMustNotExist\",\"inputs\":[{\"name\":\"valBLSPubKey\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]},{\"type\":\"error\",\"name\":\"WithdrawalAddressMismatch\",\"inputs\":[{\"name\":\"actualWithdrawalAddress\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"expectedWithdrawalAddress\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"WithdrawalAddressMustBeSet\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"WithdrawalFailed\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"WithdrawingTooSoon\",\"inputs\":[]}]", + ABI: "[{\"type\":\"constructor\",\"inputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"fallback\",\"stateMutability\":\"payable\"},{\"type\":\"receive\",\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"UPGRADE_INTERFACE_VERSION\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"string\",\"internalType\":\"string\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"acceptOwnership\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"addStake\",\"inputs\":[{\"name\":\"blsPubKeys\",\"type\":\"bytes[]\",\"internalType\":\"bytes[]\"}],\"outputs\":[],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"claimForceWithdrawnFunds\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"delegateStake\",\"inputs\":[{\"name\":\"blsPubKeys\",\"type\":\"bytes[]\",\"internalType\":\"bytes[]\"},{\"name\":\"withdrawalAddress\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"forceWithdrawalAsOwner\",\"inputs\":[{\"name\":\"blsPubKeys\",\"type\":\"bytes[]\",\"internalType\":\"bytes[]\"},{\"name\":\"withdrawalAddress\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"forceWithdrawnFunds\",\"inputs\":[{\"name\":\"withdrawalAddress\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"amountToClaim\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getAccumulatedSlashingFunds\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getBlocksTillWithdrawAllowed\",\"inputs\":[{\"name\":\"valBLSPubKey\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getStakedAmount\",\"inputs\":[{\"name\":\"valBLSPubKey\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getStakedValidator\",\"inputs\":[{\"name\":\"valBLSPubKey\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[{\"name\":\"\",\"type\":\"tuple\",\"internalType\":\"structIVanillaRegistry.StakedValidator\",\"components\":[{\"name\":\"exists\",\"type\":\"bool\",\"internalType\":\"bool\"},{\"name\":\"withdrawalAddress\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"balance\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"unstakeOccurrence\",\"type\":\"tuple\",\"internalType\":\"structBlockHeightOccurrence.Occurrence\",\"components\":[{\"name\":\"exists\",\"type\":\"bool\",\"internalType\":\"bool\"},{\"name\":\"blockHeight\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]}]}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"initialize\",\"inputs\":[{\"name\":\"_minStake\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_slashOracle\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_slashReceiver\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_unstakePeriodBlocks\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_slashingPayoutPeriodBlocks\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_owner\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"isSlashingPayoutDue\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"isUnstaking\",\"inputs\":[{\"name\":\"valBLSPubKey\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"isValidatorOptedIn\",\"inputs\":[{\"name\":\"valBLSPubKey\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"manuallyTransferSlashingFunds\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"minStake\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"owner\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"pause\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"paused\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"pendingOwner\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"proxiableUUID\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"removeWhitelistedStakers\",\"inputs\":[{\"name\":\"stakers\",\"type\":\"address[]\",\"internalType\":\"address[]\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"renounceOwnership\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setMinStake\",\"inputs\":[{\"name\":\"newMinStake\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setSlashOracle\",\"inputs\":[{\"name\":\"newSlashOracle\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setSlashReceiver\",\"inputs\":[{\"name\":\"newSlashReceiver\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setSlashingPayoutPeriodBlocks\",\"inputs\":[{\"name\":\"newSlashingPayoutPeriodBlocks\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setUnstakePeriodBlocks\",\"inputs\":[{\"name\":\"newUnstakePeriodBlocks\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"slash\",\"inputs\":[{\"name\":\"blsPubKeys\",\"type\":\"bytes[]\",\"internalType\":\"bytes[]\"},{\"name\":\"payoutIfDue\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"slashOracle\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"slashingFundsTracker\",\"inputs\":[],\"outputs\":[{\"name\":\"recipient\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"accumulatedAmount\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"lastPayoutBlock\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"payoutPeriodBlocks\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"stake\",\"inputs\":[{\"name\":\"blsPubKeys\",\"type\":\"bytes[]\",\"internalType\":\"bytes[]\"}],\"outputs\":[],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"stakedValidators\",\"inputs\":[{\"name\":\"\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[{\"name\":\"exists\",\"type\":\"bool\",\"internalType\":\"bool\"},{\"name\":\"withdrawalAddress\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"balance\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"unstakeOccurrence\",\"type\":\"tuple\",\"internalType\":\"structBlockHeightOccurrence.Occurrence\",\"components\":[{\"name\":\"exists\",\"type\":\"bool\",\"internalType\":\"bool\"},{\"name\":\"blockHeight\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"transferOwnership\",\"inputs\":[{\"name\":\"newOwner\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"unpause\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"unstake\",\"inputs\":[{\"name\":\"blsPubKeys\",\"type\":\"bytes[]\",\"internalType\":\"bytes[]\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"unstakePeriodBlocks\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"upgradeToAndCall\",\"inputs\":[{\"name\":\"newImplementation\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"whitelistStakers\",\"inputs\":[{\"name\":\"stakers\",\"type\":\"address[]\",\"internalType\":\"address[]\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"whitelistedStakers\",\"inputs\":[{\"name\":\"staker\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"whitelisted\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"withdraw\",\"inputs\":[{\"name\":\"blsPubKeys\",\"type\":\"bytes[]\",\"internalType\":\"bytes[]\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"event\",\"name\":\"FeeTransfer\",\"inputs\":[{\"name\":\"amount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"},{\"name\":\"recipient\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Initialized\",\"inputs\":[{\"name\":\"version\",\"type\":\"uint64\",\"indexed\":false,\"internalType\":\"uint64\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"MinStakeSet\",\"inputs\":[{\"name\":\"msgSender\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newMinStake\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"OwnershipTransferStarted\",\"inputs\":[{\"name\":\"previousOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"OwnershipTransferred\",\"inputs\":[{\"name\":\"previousOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Paused\",\"inputs\":[{\"name\":\"account\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"SlashOracleSet\",\"inputs\":[{\"name\":\"msgSender\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newSlashOracle\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"SlashReceiverSet\",\"inputs\":[{\"name\":\"msgSender\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newSlashReceiver\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Slashed\",\"inputs\":[{\"name\":\"msgSender\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"slashReceiver\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"withdrawalAddress\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"valBLSPubKey\",\"type\":\"bytes\",\"indexed\":false,\"internalType\":\"bytes\"},{\"name\":\"amount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"SlashingPayoutPeriodBlocksSet\",\"inputs\":[{\"name\":\"msgSender\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newSlashingPayoutPeriodBlocks\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"StakeAdded\",\"inputs\":[{\"name\":\"msgSender\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"withdrawalAddress\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"valBLSPubKey\",\"type\":\"bytes\",\"indexed\":false,\"internalType\":\"bytes\"},{\"name\":\"amount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"},{\"name\":\"newBalance\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"StakeWithdrawn\",\"inputs\":[{\"name\":\"msgSender\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"withdrawalAddress\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"valBLSPubKey\",\"type\":\"bytes\",\"indexed\":false,\"internalType\":\"bytes\"},{\"name\":\"amount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Staked\",\"inputs\":[{\"name\":\"msgSender\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"withdrawalAddress\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"valBLSPubKey\",\"type\":\"bytes\",\"indexed\":false,\"internalType\":\"bytes\"},{\"name\":\"amount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"StakerRemovedFromWhitelist\",\"inputs\":[{\"name\":\"msgSender\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"staker\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"StakerWhitelisted\",\"inputs\":[{\"name\":\"msgSender\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"staker\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"TotalStakeWithdrawn\",\"inputs\":[{\"name\":\"msgSender\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"withdrawalAddress\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"totalAmount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Unpaused\",\"inputs\":[{\"name\":\"account\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"UnstakePeriodBlocksSet\",\"inputs\":[{\"name\":\"msgSender\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newUnstakePeriodBlocks\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Unstaked\",\"inputs\":[{\"name\":\"msgSender\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"withdrawalAddress\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"valBLSPubKey\",\"type\":\"bytes\",\"indexed\":false,\"internalType\":\"bytes\"},{\"name\":\"amount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Upgraded\",\"inputs\":[{\"name\":\"implementation\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"error\",\"name\":\"AddressEmptyCode\",\"inputs\":[{\"name\":\"target\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"AtLeastOneRecipientRequired\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ERC1967InvalidImplementation\",\"inputs\":[{\"name\":\"implementation\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"ERC1967NonPayable\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"EnforcedPause\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ExpectedPause\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"FailedInnerCall\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"FeeRecipientIsZero\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidBLSPubKeyLength\",\"inputs\":[{\"name\":\"expected\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"actual\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"type\":\"error\",\"name\":\"InvalidFallback\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidInitialization\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidReceive\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"MinStakeMustBePositive\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"MustUnstakeToWithdraw\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"NoFundsToWithdraw\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"NotInitializing\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"OwnableInvalidOwner\",\"inputs\":[{\"name\":\"owner\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"OwnableUnauthorizedAccount\",\"inputs\":[{\"name\":\"account\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"PayoutPeriodMustBePositive\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"SenderIsNotSlashOracle\",\"inputs\":[{\"name\":\"sender\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"slashOracle\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"SenderIsNotWhitelistedStaker\",\"inputs\":[{\"name\":\"sender\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"SenderIsNotWithdrawalAddress\",\"inputs\":[{\"name\":\"sender\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"withdrawalAddress\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"SlashAmountMustBeLessThanMinStake\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"SlashAmountMustBePositive\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"SlashOracleMustBeSet\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"SlashReceiverMustBeSet\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"SlashingPayoutPeriodMustBePositive\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"SlashingTransferFailed\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"StakeTooLowForNumberOfKeys\",\"inputs\":[{\"name\":\"msgValue\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"required\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"type\":\"error\",\"name\":\"StakerAlreadyWhitelisted\",\"inputs\":[{\"name\":\"staker\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"StakerNotWhitelisted\",\"inputs\":[{\"name\":\"staker\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"TransferToRecipientFailed\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"UUPSUnauthorizedCallContext\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"UUPSUnsupportedProxiableUUID\",\"inputs\":[{\"name\":\"slot\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]},{\"type\":\"error\",\"name\":\"UnstakePeriodMustBePositive\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ValidatorCannotBeUnstaking\",\"inputs\":[{\"name\":\"valBLSPubKey\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]},{\"type\":\"error\",\"name\":\"ValidatorRecordMustExist\",\"inputs\":[{\"name\":\"valBLSPubKey\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]},{\"type\":\"error\",\"name\":\"ValidatorRecordMustNotExist\",\"inputs\":[{\"name\":\"valBLSPubKey\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]},{\"type\":\"error\",\"name\":\"WithdrawalAddressMismatch\",\"inputs\":[{\"name\":\"actualWithdrawalAddress\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"expectedWithdrawalAddress\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"WithdrawalAddressMustBeSet\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"WithdrawalFailed\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"WithdrawingTooSoon\",\"inputs\":[]}]", } // VanillaregistryABI is the input ABI used to generate the binding from. @@ -800,6 +800,37 @@ func (_Vanillaregistry *VanillaregistryCallerSession) UnstakePeriodBlocks() (*bi return _Vanillaregistry.Contract.UnstakePeriodBlocks(&_Vanillaregistry.CallOpts) } +// WhitelistedStakers is a free data retrieval call binding the contract method 0xfdaf17f0. +// +// Solidity: function whitelistedStakers(address staker) view returns(bool whitelisted) +func (_Vanillaregistry *VanillaregistryCaller) WhitelistedStakers(opts *bind.CallOpts, staker common.Address) (bool, error) { + var out []interface{} + err := _Vanillaregistry.contract.Call(opts, &out, "whitelistedStakers", staker) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// WhitelistedStakers is a free data retrieval call binding the contract method 0xfdaf17f0. +// +// Solidity: function whitelistedStakers(address staker) view returns(bool whitelisted) +func (_Vanillaregistry *VanillaregistrySession) WhitelistedStakers(staker common.Address) (bool, error) { + return _Vanillaregistry.Contract.WhitelistedStakers(&_Vanillaregistry.CallOpts, staker) +} + +// WhitelistedStakers is a free data retrieval call binding the contract method 0xfdaf17f0. +// +// Solidity: function whitelistedStakers(address staker) view returns(bool whitelisted) +func (_Vanillaregistry *VanillaregistryCallerSession) WhitelistedStakers(staker common.Address) (bool, error) { + return _Vanillaregistry.Contract.WhitelistedStakers(&_Vanillaregistry.CallOpts, staker) +} + // AcceptOwnership is a paid mutator transaction binding the contract method 0x79ba5097. // // Solidity: function acceptOwnership() returns() @@ -968,6 +999,27 @@ func (_Vanillaregistry *VanillaregistryTransactorSession) Pause() (*types.Transa return _Vanillaregistry.Contract.Pause(&_Vanillaregistry.TransactOpts) } +// RemoveWhitelistedStakers is a paid mutator transaction binding the contract method 0x5158c9fa. +// +// Solidity: function removeWhitelistedStakers(address[] stakers) returns() +func (_Vanillaregistry *VanillaregistryTransactor) RemoveWhitelistedStakers(opts *bind.TransactOpts, stakers []common.Address) (*types.Transaction, error) { + return _Vanillaregistry.contract.Transact(opts, "removeWhitelistedStakers", stakers) +} + +// RemoveWhitelistedStakers is a paid mutator transaction binding the contract method 0x5158c9fa. +// +// Solidity: function removeWhitelistedStakers(address[] stakers) returns() +func (_Vanillaregistry *VanillaregistrySession) RemoveWhitelistedStakers(stakers []common.Address) (*types.Transaction, error) { + return _Vanillaregistry.Contract.RemoveWhitelistedStakers(&_Vanillaregistry.TransactOpts, stakers) +} + +// RemoveWhitelistedStakers is a paid mutator transaction binding the contract method 0x5158c9fa. +// +// Solidity: function removeWhitelistedStakers(address[] stakers) returns() +func (_Vanillaregistry *VanillaregistryTransactorSession) RemoveWhitelistedStakers(stakers []common.Address) (*types.Transaction, error) { + return _Vanillaregistry.Contract.RemoveWhitelistedStakers(&_Vanillaregistry.TransactOpts, stakers) +} + // RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6. // // Solidity: function renounceOwnership() returns() @@ -1220,6 +1272,27 @@ func (_Vanillaregistry *VanillaregistryTransactorSession) UpgradeToAndCall(newIm return _Vanillaregistry.Contract.UpgradeToAndCall(&_Vanillaregistry.TransactOpts, newImplementation, data) } +// WhitelistStakers is a paid mutator transaction binding the contract method 0x95ebffdb. +// +// Solidity: function whitelistStakers(address[] stakers) returns() +func (_Vanillaregistry *VanillaregistryTransactor) WhitelistStakers(opts *bind.TransactOpts, stakers []common.Address) (*types.Transaction, error) { + return _Vanillaregistry.contract.Transact(opts, "whitelistStakers", stakers) +} + +// WhitelistStakers is a paid mutator transaction binding the contract method 0x95ebffdb. +// +// Solidity: function whitelistStakers(address[] stakers) returns() +func (_Vanillaregistry *VanillaregistrySession) WhitelistStakers(stakers []common.Address) (*types.Transaction, error) { + return _Vanillaregistry.Contract.WhitelistStakers(&_Vanillaregistry.TransactOpts, stakers) +} + +// WhitelistStakers is a paid mutator transaction binding the contract method 0x95ebffdb. +// +// Solidity: function whitelistStakers(address[] stakers) returns() +func (_Vanillaregistry *VanillaregistryTransactorSession) WhitelistStakers(stakers []common.Address) (*types.Transaction, error) { + return _Vanillaregistry.Contract.WhitelistStakers(&_Vanillaregistry.TransactOpts, stakers) +} + // Withdraw is a paid mutator transaction binding the contract method 0xdcb1edcb. // // Solidity: function withdraw(bytes[] blsPubKeys) returns() @@ -3212,6 +3285,296 @@ func (_Vanillaregistry *VanillaregistryFilterer) ParseStaked(log types.Log) (*Va return event, nil } +// VanillaregistryStakerRemovedFromWhitelistIterator is returned from FilterStakerRemovedFromWhitelist and is used to iterate over the raw logs and unpacked data for StakerRemovedFromWhitelist events raised by the Vanillaregistry contract. +type VanillaregistryStakerRemovedFromWhitelistIterator struct { + Event *VanillaregistryStakerRemovedFromWhitelist // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *VanillaregistryStakerRemovedFromWhitelistIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(VanillaregistryStakerRemovedFromWhitelist) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(VanillaregistryStakerRemovedFromWhitelist) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *VanillaregistryStakerRemovedFromWhitelistIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *VanillaregistryStakerRemovedFromWhitelistIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// VanillaregistryStakerRemovedFromWhitelist represents a StakerRemovedFromWhitelist event raised by the Vanillaregistry contract. +type VanillaregistryStakerRemovedFromWhitelist struct { + MsgSender common.Address + Staker common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterStakerRemovedFromWhitelist is a free log retrieval operation binding the contract event 0x79fccea9ca325c6c715589de68546a63a35425178d1a8b1436bef7f7a76087a4. +// +// Solidity: event StakerRemovedFromWhitelist(address indexed msgSender, address staker) +func (_Vanillaregistry *VanillaregistryFilterer) FilterStakerRemovedFromWhitelist(opts *bind.FilterOpts, msgSender []common.Address) (*VanillaregistryStakerRemovedFromWhitelistIterator, error) { + + var msgSenderRule []interface{} + for _, msgSenderItem := range msgSender { + msgSenderRule = append(msgSenderRule, msgSenderItem) + } + + logs, sub, err := _Vanillaregistry.contract.FilterLogs(opts, "StakerRemovedFromWhitelist", msgSenderRule) + if err != nil { + return nil, err + } + return &VanillaregistryStakerRemovedFromWhitelistIterator{contract: _Vanillaregistry.contract, event: "StakerRemovedFromWhitelist", logs: logs, sub: sub}, nil +} + +// WatchStakerRemovedFromWhitelist is a free log subscription operation binding the contract event 0x79fccea9ca325c6c715589de68546a63a35425178d1a8b1436bef7f7a76087a4. +// +// Solidity: event StakerRemovedFromWhitelist(address indexed msgSender, address staker) +func (_Vanillaregistry *VanillaregistryFilterer) WatchStakerRemovedFromWhitelist(opts *bind.WatchOpts, sink chan<- *VanillaregistryStakerRemovedFromWhitelist, msgSender []common.Address) (event.Subscription, error) { + + var msgSenderRule []interface{} + for _, msgSenderItem := range msgSender { + msgSenderRule = append(msgSenderRule, msgSenderItem) + } + + logs, sub, err := _Vanillaregistry.contract.WatchLogs(opts, "StakerRemovedFromWhitelist", msgSenderRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(VanillaregistryStakerRemovedFromWhitelist) + if err := _Vanillaregistry.contract.UnpackLog(event, "StakerRemovedFromWhitelist", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseStakerRemovedFromWhitelist is a log parse operation binding the contract event 0x79fccea9ca325c6c715589de68546a63a35425178d1a8b1436bef7f7a76087a4. +// +// Solidity: event StakerRemovedFromWhitelist(address indexed msgSender, address staker) +func (_Vanillaregistry *VanillaregistryFilterer) ParseStakerRemovedFromWhitelist(log types.Log) (*VanillaregistryStakerRemovedFromWhitelist, error) { + event := new(VanillaregistryStakerRemovedFromWhitelist) + if err := _Vanillaregistry.contract.UnpackLog(event, "StakerRemovedFromWhitelist", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// VanillaregistryStakerWhitelistedIterator is returned from FilterStakerWhitelisted and is used to iterate over the raw logs and unpacked data for StakerWhitelisted events raised by the Vanillaregistry contract. +type VanillaregistryStakerWhitelistedIterator struct { + Event *VanillaregistryStakerWhitelisted // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *VanillaregistryStakerWhitelistedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(VanillaregistryStakerWhitelisted) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(VanillaregistryStakerWhitelisted) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *VanillaregistryStakerWhitelistedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *VanillaregistryStakerWhitelistedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// VanillaregistryStakerWhitelisted represents a StakerWhitelisted event raised by the Vanillaregistry contract. +type VanillaregistryStakerWhitelisted struct { + MsgSender common.Address + Staker common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterStakerWhitelisted is a free log retrieval operation binding the contract event 0xc94be990bcb425c41f84d9928ad9082894fb4f1f1d2508bc088d3a6a3e4059db. +// +// Solidity: event StakerWhitelisted(address indexed msgSender, address staker) +func (_Vanillaregistry *VanillaregistryFilterer) FilterStakerWhitelisted(opts *bind.FilterOpts, msgSender []common.Address) (*VanillaregistryStakerWhitelistedIterator, error) { + + var msgSenderRule []interface{} + for _, msgSenderItem := range msgSender { + msgSenderRule = append(msgSenderRule, msgSenderItem) + } + + logs, sub, err := _Vanillaregistry.contract.FilterLogs(opts, "StakerWhitelisted", msgSenderRule) + if err != nil { + return nil, err + } + return &VanillaregistryStakerWhitelistedIterator{contract: _Vanillaregistry.contract, event: "StakerWhitelisted", logs: logs, sub: sub}, nil +} + +// WatchStakerWhitelisted is a free log subscription operation binding the contract event 0xc94be990bcb425c41f84d9928ad9082894fb4f1f1d2508bc088d3a6a3e4059db. +// +// Solidity: event StakerWhitelisted(address indexed msgSender, address staker) +func (_Vanillaregistry *VanillaregistryFilterer) WatchStakerWhitelisted(opts *bind.WatchOpts, sink chan<- *VanillaregistryStakerWhitelisted, msgSender []common.Address) (event.Subscription, error) { + + var msgSenderRule []interface{} + for _, msgSenderItem := range msgSender { + msgSenderRule = append(msgSenderRule, msgSenderItem) + } + + logs, sub, err := _Vanillaregistry.contract.WatchLogs(opts, "StakerWhitelisted", msgSenderRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(VanillaregistryStakerWhitelisted) + if err := _Vanillaregistry.contract.UnpackLog(event, "StakerWhitelisted", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseStakerWhitelisted is a log parse operation binding the contract event 0xc94be990bcb425c41f84d9928ad9082894fb4f1f1d2508bc088d3a6a3e4059db. +// +// Solidity: event StakerWhitelisted(address indexed msgSender, address staker) +func (_Vanillaregistry *VanillaregistryFilterer) ParseStakerWhitelisted(log types.Log) (*VanillaregistryStakerWhitelisted, error) { + event := new(VanillaregistryStakerWhitelisted) + if err := _Vanillaregistry.contract.UnpackLog(event, "StakerWhitelisted", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + // VanillaregistryTotalStakeWithdrawnIterator is returned from FilterTotalStakeWithdrawn and is used to iterate over the raw logs and unpacked data for TotalStakeWithdrawn events raised by the Vanillaregistry contract. type VanillaregistryTotalStakeWithdrawnIterator struct { Event *VanillaregistryTotalStakeWithdrawn // Event containing the contract specifics and raw log diff --git a/contracts/contracts/interfaces/IVanillaRegistry.sol b/contracts/contracts/interfaces/IVanillaRegistry.sol index 722da86a5..e978cdc8a 100644 --- a/contracts/contracts/interfaces/IVanillaRegistry.sol +++ b/contracts/contracts/interfaces/IVanillaRegistry.sol @@ -48,6 +48,12 @@ interface IVanillaRegistry { /// @dev Event emitted when the slashing payout period blocks parameter is set. event SlashingPayoutPeriodBlocksSet(address indexed msgSender, uint256 newSlashingPayoutPeriodBlocks); + /// @dev Event emitted when a staker is whitelisted. + event StakerWhitelisted(address indexed msgSender, address staker); + + /// @dev Event emitted when a staker is removed from the whitelist. + event StakerRemovedFromWhitelist(address indexed msgSender, address staker); + error ValidatorRecordMustExist(bytes valBLSPubKey); error ValidatorRecordMustNotExist(bytes valBLSPubKey); error ValidatorCannotBeUnstaking(bytes valBLSPubKey); @@ -70,6 +76,9 @@ interface IVanillaRegistry { error SlashReceiverMustBeSet(); error UnstakePeriodMustBePositive(); error SlashingPayoutPeriodMustBePositive(); + error SenderIsNotWhitelistedStaker(address sender); + error StakerAlreadyWhitelisted(address staker); + error StakerNotWhitelisted(address staker); /// @dev Initializes the contract with the provided parameters. function initialize( diff --git a/contracts/contracts/validator-registry/VanillaRegistry.sol b/contracts/contracts/validator-registry/VanillaRegistry.sol index aeebcb63c..34679a0f3 100644 --- a/contracts/contracts/validator-registry/VanillaRegistry.sol +++ b/contracts/contracts/validator-registry/VanillaRegistry.sol @@ -31,6 +31,12 @@ contract VanillaRegistry is IVanillaRegistry, VanillaRegistryStorage, _; } + /// @dev Modifier to confirm the sender is whitelisted. + modifier onlyWhitelistedStaker() { + require(whitelistedStakers[msg.sender], IVanillaRegistry.SenderIsNotWhitelistedStaker(msg.sender)); + _; + } + /// @dev See https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#initializing_the_implementation_contract /// @custom:oz-upgrades-unsafe-allow constructor constructor() { @@ -69,7 +75,7 @@ contract VanillaRegistry is IVanillaRegistry, VanillaRegistryStorage, * @param blsPubKeys The validator BLS public keys to stake. */ function stake(bytes[] calldata blsPubKeys) external payable - onlyValidBLSPubKeys(blsPubKeys) whenNotPaused() { + onlyValidBLSPubKeys(blsPubKeys) onlyWhitelistedStaker() whenNotPaused() { _stake(blsPubKeys, msg.sender); } @@ -90,7 +96,7 @@ contract VanillaRegistry is IVanillaRegistry, VanillaRegistryStorage, * @dev A staking entry must already exist for each provided BLS pubkey. * @param blsPubKeys The BLS public keys to add stake to. */ - function addStake(bytes[] calldata blsPubKeys) external payable whenNotPaused() { + function addStake(bytes[] calldata blsPubKeys) external payable onlyWhitelistedStaker() whenNotPaused() { _addStake(blsPubKeys); } @@ -180,6 +186,26 @@ contract VanillaRegistry is IVanillaRegistry, VanillaRegistryStorage, FeePayout.transferToRecipient(slashingFundsTracker); } + /// @dev Enables the owner to whitelist stakers. + function whitelistStakers(address[] calldata stakers) external onlyOwner { + uint256 len = stakers.length; + for (uint256 i = 0; i < len; ++i) { + require(!whitelistedStakers[stakers[i]], IVanillaRegistry.StakerAlreadyWhitelisted(stakers[i])); + whitelistedStakers[stakers[i]] = true; + emit StakerWhitelisted(msg.sender, stakers[i]); + } + } + + /// @dev Enables the owner to remove stakers from the whitelist. + function removeWhitelistedStakers(address[] calldata stakers) external onlyOwner { + uint256 len = stakers.length; + for (uint256 i = 0; i < len; ++i) { + require(whitelistedStakers[stakers[i]], IVanillaRegistry.StakerNotWhitelisted(stakers[i])); + whitelistedStakers[stakers[i]] = false; + emit StakerRemovedFromWhitelist(msg.sender, stakers[i]); + } + } + /// @dev Returns true if a validator is considered "opted-in" to mev-commit via this registry. function isValidatorOptedIn(bytes calldata valBLSPubKey) external view returns (bool) { return _isValidatorOptedIn(valBLSPubKey); diff --git a/contracts/contracts/validator-registry/VanillaRegistryStorage.sol b/contracts/contracts/validator-registry/VanillaRegistryStorage.sol index 8ec7149eb..b9b3f5775 100644 --- a/contracts/contracts/validator-registry/VanillaRegistryStorage.sol +++ b/contracts/contracts/validator-registry/VanillaRegistryStorage.sol @@ -26,6 +26,9 @@ contract VanillaRegistryStorage { /// @dev Mapping of withdrawal addresses to claimable ETH that was force withdrawn by the owner. mapping(address withdrawalAddress => uint256 amountToClaim) public forceWithdrawnFunds; + /// @dev Mapping of staker addresses to whether they are whitelisted. + mapping(address staker => bool whitelisted) public whitelistedStakers; + /// @dev See https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#storage-gaps uint256[48] private __gap; } diff --git a/contracts/test/validator-registry/VanillaRegistryTest.sol b/contracts/test/validator-registry/VanillaRegistryTest.sol index af7405cea..367170772 100644 --- a/contracts/test/validator-registry/VanillaRegistryTest.sol +++ b/contracts/test/validator-registry/VanillaRegistryTest.sol @@ -66,8 +66,16 @@ contract VanillaRegistryTest is Test { bytes[] memory validators = new bytes[](1); validators[0] = user1BLSKey; - vm.startPrank(user1); + vm.expectRevert(abi.encodeWithSelector(IVanillaRegistry.SenderIsNotWhitelistedStaker.selector, user1)); + vm.prank(user1); + validatorRegistry.stake{value: MIN_STAKE}(validators); + + vm.prank(owner); + address[] memory stakers = new address[](1); + stakers[0] = user1; + validatorRegistry.whitelistStakers(stakers); + vm.startPrank(user1); vm.expectEmit(true, true, true, true); emit Staked(user1, user1, user1BLSKey, MIN_STAKE); validatorRegistry.stake{value: MIN_STAKE}(validators); @@ -80,7 +88,44 @@ contract VanillaRegistryTest is Test { assertTrue(validatorRegistry.isValidatorOptedIn(user1BLSKey)); } + function testStakeAfterRemovedFromWhitelist() public { + testSelfStake(); + + vm.prank(owner); + address[] memory stakers = new address[](1); + stakers[0] = user1; + validatorRegistry.removeWhitelistedStakers(stakers); + + bytes[] memory validators = new bytes[](1); + validators[0] = user2BLSKey; + + vm.startPrank(user1); + vm.expectRevert(abi.encodeWithSelector(IVanillaRegistry.SenderIsNotWhitelistedStaker.selector, user1)); + validatorRegistry.stake{value: MIN_STAKE}(validators); + vm.stopPrank(); + + // works once again after whitelisting + vm.prank(owner); + validatorRegistry.whitelistStakers(stakers); + + vm.startPrank(user1); + vm.expectEmit(true, true, true, true); + emit Staked(user1, user1, user2BLSKey, MIN_STAKE); + validatorRegistry.stake{value: MIN_STAKE}(validators); + vm.stopPrank(); + + assertEq(address(user1).balance, 7 ether); + assertEq(validatorRegistry.getStakedAmount(user1BLSKey), MIN_STAKE); + assertTrue(validatorRegistry.isValidatorOptedIn(user1BLSKey)); + } + function testMultiStake() public { + vm.prank(owner); + address[] memory stakers = new address[](2); + stakers[0] = user1; + stakers[1] = user2; + validatorRegistry.whitelistStakers(stakers); + bytes[] memory validators = new bytes[](2); validators[0] = user1BLSKey; validators[1] = user2BLSKey; @@ -133,6 +178,12 @@ contract VanillaRegistryTest is Test { } function testAddStake() public { + vm.prank(owner); + address[] memory stakers = new address[](2); + stakers[0] = user1; + stakers[1] = user2; + validatorRegistry.whitelistStakers(stakers); + vm.deal(user1, 10 ether); assertEq(user1.balance, 10 ether); @@ -195,6 +246,12 @@ contract VanillaRegistryTest is Test { function testUnathorizedMultiUnstake() public { testSelfStake(); + + vm.prank(owner); + address[] memory stakers = new address[](1); + stakers[0] = user2; + validatorRegistry.whitelistStakers(stakers); + bytes[] memory validators = new bytes[](1); validators[0] = user2BLSKey; vm.deal(user2, MIN_STAKE); @@ -358,6 +415,11 @@ contract VanillaRegistryTest is Test { validatorRegistry.stake{value: MIN_STAKE}(validators); vm.stopPrank(); + vm.prank(owner); + address[] memory stakers = new address[](1); + stakers[0] = user2; + validatorRegistry.whitelistStakers(stakers); + vm.deal(user2, 10 ether); vm.startPrank(user2); vm.expectRevert(abi.encodeWithSelector(IVanillaRegistry.ValidatorRecordMustNotExist.selector, user1BLSKey)); @@ -424,6 +486,11 @@ contract VanillaRegistryTest is Test { vm.prank(SLASH_ORACLE); validatorRegistry.slash(validators, true); + vm.prank(owner); + address[] memory stakers = new address[](1); + stakers[0] = user1; + validatorRegistry.whitelistStakers(stakers); + vm.deal(user1, 2 ether); vm.startPrank(user1); uint256 stakeAmount = MIN_STAKE+1; @@ -590,11 +657,16 @@ contract VanillaRegistryTest is Test { function testManualPayout() public { testBatchedSlashing(); + vm.prank(owner); + address[] memory stakers = new address[](1); + address user3 = vm.addr(0x23333); + stakers[0] = user3; + validatorRegistry.whitelistStakers(stakers); + vm.roll(10000); bytes[] memory validators = new bytes[](1); validators[0] = user3BLSKey; - address user3 = vm.addr(0x23333); vm.deal(user3, 10 ether); vm.startPrank(user3); vm.expectEmit(true, true, true, true); @@ -737,6 +809,11 @@ contract VanillaRegistryTest is Test { function testStakingCycle() public { testUnstakeWaitThenWithdraw(); + vm.prank(owner); + address[] memory stakers = new address[](1); + stakers[0] = user1; + validatorRegistry.removeWhitelistedStakers(stakers); + // Reset user1 balance for next cycle vm.prank(user1); (bool sent, ) = user2.call{value: 9 ether}(""); @@ -807,6 +884,11 @@ contract VanillaRegistryTest is Test { validatorRegistry.pause(); vm.stopPrank(); + vm.prank(owner); + address[] memory stakers = new address[](1); + stakers[0] = user1; + validatorRegistry.whitelistStakers(stakers); + bytes[] memory validators = new bytes[](1); validators[0] = user1BLSKey; vm.startPrank(user1); @@ -857,6 +939,11 @@ contract VanillaRegistryTest is Test { } function testPrecisionLossPrevention() public { + vm.prank(owner); + address[] memory stakers = new address[](1); + stakers[0] = user1; + validatorRegistry.whitelistStakers(stakers); + vm.prank(owner); validatorRegistry.setMinStake(1 wei); @@ -1042,6 +1129,11 @@ contract VanillaRegistryTest is Test { } function testStakeWithDuplicateBlsPubkeys() public { + vm.prank(owner); + address[] memory stakers = new address[](2); + stakers[0] = user1; + validatorRegistry.whitelistStakers(stakers); + bytes[] memory validators = new bytes[](3); validators[0] = user1BLSKey; validators[1] = user2BLSKey; @@ -1072,4 +1164,23 @@ contract VanillaRegistryTest is Test { emit Staked(user1, user1, user3BLSKey, MIN_STAKE); validatorRegistry.stake{value: 3 * MIN_STAKE}(validators); } + + function testCannotWhitelistStakerTwice() public { + vm.prank(owner); + address[] memory stakers = new address[](1); + stakers[0] = user1; + validatorRegistry.whitelistStakers(stakers); + + vm.prank(owner); + vm.expectRevert(abi.encodeWithSelector(IVanillaRegistry.StakerAlreadyWhitelisted.selector, user1)); + validatorRegistry.whitelistStakers(stakers); + } + + function testCannotRemoveNonWhitelistedStakerfromWhitelist() public { + vm.prank(owner); + address[] memory stakers = new address[](1); + stakers[0] = user1; + vm.expectRevert(abi.encodeWithSelector(IVanillaRegistry.StakerNotWhitelisted.selector, user1)); + validatorRegistry.removeWhitelistedStakers(stakers); + } } diff --git a/contracts/test/validator-registry/rewards/RewardManagerTest.sol b/contracts/test/validator-registry/rewards/RewardManagerTest.sol index f757c018b..dab77bcff 100644 --- a/contracts/test/validator-registry/rewards/RewardManagerTest.sol +++ b/contracts/test/validator-registry/rewards/RewardManagerTest.sol @@ -498,6 +498,11 @@ contract RewardManagerTest is Test { function testAutoClaimBlacklist() public { CanRevert canRevert = new CanRevert(); + address[] memory stakers = new address[](1); + stakers[0] = address(canRevert); + vm.prank(vanillaRegistry.owner()); + vanillaRegistry.whitelistStakers(stakers); + vm.deal(address(canRevert), 9 ether); assertEq(address(canRevert).balance, 9 ether); bytes[] memory validators = new bytes[](1);