NodeOperatorRegistry

Link to NodeOperatorRegistry source code on Ethereum

The NodeOperatorRegistry contract is the core contract that allows node operators to participate in the K9 Finance liquid staking protocol. Node Operators participate on the protocol as validators and get rewarded for their work. A Node Operator gets added to the Registry by the DAO. Validator reward is distributed evenly amongst all active operators. The contract contains a list of operators, their public keys, and the logic for managing their state.

Roles

The NodeOperatorRegistry contract has the following roles

Name
Description

DAO_ROLE

DAO role

PAUSE_ROLE

Allows to pause the contract

UNPAUSE_ROLE

Allows to unpause the contract

ADD_NODE_OPERATOR_ROLE

Allows to add new node operator the contract

REMOVE_NODE_OPERATOR_ROLE

Allows to remove a node operator the contract

Variables

IStakeManager public stakeManager; - Shibarium stakeManager contract address.

IKnBONE public knBONE; - knBONE contract address.

bytes32 public constant DAO_ROLE = keccak256("DAO_ROLE"); - dao role identifier.

bytes32 public constant PAUSE_ROLE = keccak256("PAUSE_ROLE"); - pauser role identifier.

bytes32 public constant UNPAUSE_ROLE = keccak256("UNPAUSE_ROLE"); - unpauser role identifier.

bytes32 public constant ADD_NODE_OPERATOR_ROLE = keccak256("ADD_NODE_OPERATOR_ROLE"); - role identifier of adding node operator authority

bytes32 public constant REMOVE_NODE_OPERATOR_ROLE = keccak256("REMOVE_NODE_OPERATOR_ROLE"); - role identifier of removing node operator authority.

uint256 public DISTANCE_THRESHOLD_PERCENTS; - the maximum number (percentage) at which the system considers itself balanced. The distance will be 100 in case where the operator’s minimum stake is equal to the maximum. The distance is calculated as (max * 100) / min.

uint256 public MAX_WITHDRAW_PERCENTAGE_PER_REBALANCE; - the maximum percentage that can be withdrawn from the total delegated amount when rebalancing the system.

uint8 public MIN_REQUEST_WITHDRAW_RANGE_PERCENTS; - a percentage added to the withdrawal percentage when withdrawing from validators. Applicable only when the system is in a balanced state. It is used to “split” the withdrawal between validators.

Does not affect the withdrawal amount.

Example: the total stake of the system is 100 BONE and there are 4 active validators. A user wants to withdraw 10 BONE, i.e. 10%.

totalValidatorToWithdrawFrom =
            (((withdrawAmountPercentage + MIN_REQUEST_WITHDRAW_RANGE_PERCENTS) *
                length) / 100) +
            1;

Without this extra percentage value, user would have withdrawn from one validator:

totalValidatorToWithdrawFrom = (10 + 0) * 4 / 100 + 1 = 1

BUT if this value is applied and suppose it is equal to 15%, then he will withdraw from two validators.

totalValidatorToWithdrawFrom = (10 + 15) * 4 / 100 + 1 = 2

uint256[] public validatorIds; - IDs of all validators in the protocol

mapping(uint256 => address) public validatorIdToRewardAddress; - returns rewardAddress for each validator ID

mapping(address => uint256) public validatorRewardAddressToId; - returns validator ID for each rewardAddress of the validator

Structs

NodeOperatorStatus

Node operator statuses

Name
Description

INACTIVE

When the node operator is INACTIVE

ACTIVE

When the node operator is ACTIVE

JAILED

When the node operator is JAILED

EJECTED

When the node operator is EJECTED

UNSTAKED

When the node operator is UNSTAKED

enum NodeOperatorRegistryStatus {
        INACTIVE,
        ACTIVE,
        JAILED,
        EJECTED,
        UNSTAKED
}

FullNodeOperatorRegistry

Name
Description

validatorId

Shibarium validator id

commissionRate

The commission rate applied by the validator on Shibarium stakeManager

validatorShare

The validatorShare contract address

rewardAddress

The validator reward address

delegation

The validator delegation status on Shibarium stakeManager

status

The validator status

struct FullNodeOperatorRegistry {
        uint256 validatorId;
        uint256 commissionRate;
        address validatorShare;
        address rewardAddress;
        bool delegation;
        NodeOperatorRegistryStatus status;
}

ValidatorData

The node operator data

Name
Description

validatorShare

The validatorShare contract address of the validator

rewardAddress

The validator reward address

struct ValidatorData {
        address validatorShare;
        address rewardAddress;
}

Events

event AddNodeOperator(uint256 validatorId, address rewardAddress); - new validator was added to the protocol event RemoveNodeOperator(uint256 validatorId, address rewardAddress); - validator was removed from the protocol event RemoveInvalidNodeOperator(uint256 validatorId, address rewardAddress); - an invalid validator was removed from the protocol event SetKnBONEAddress(address oldKnBONE, address newKnBONE); - KnBONE contract address is set event SetRewardAddress(uint256 validatorId, address oldRewardAddress,address newRewardAddress); - rewardAddress is set for the validator event SetDistanceThreshold(uint256 oldDistanceThreshold, uint256 newDistanceThreshold); - DISTANCE_THRESHOLD_PERCENTS is set event SetMinRequestWithdrawRange(uint8 oldMinRequestWithdrawRange, uint8 newMinRequestWithdrawRange); - MIN_REQUEST_WITHDRAW_RANGE_PERCENTS is set event SetMaxWithdrawPercentagePerRebalance(uint256 oldMaxWithdrawPercentagePerRebalance, uint256 newMaxWithdrawPercentagePerRebalance); - MAX_WITHDRAW_PERCENTAGE_PER_REBALANCE is set event ExitNodeOperator(uint256 validatorId, address rewardAddress); - the validator removed himself from the protocol

View functions

listDelegatedNodeOperators

function listDelegatedNodeOperators()
        external
        view
        returns (ValidatorData[] memory)

Returns an array of structures ValidatorData for all validators whose status ACTIVE and whose ValidatorShare.delegation() returns true.

listWithdrawNodeOperators

function listWithdrawNodeOperators()
        external
        view
        returns (ValidatorData[] memory)

Returns an array of ValidatorData structures for all validators whose status is not INACTIVE.

getValidatorsDelegationAmount

function getValidatorsDelegationAmount(uint256 _amountToDelegate)
        external
        view
        returns (
            ValidatorData[] memory validators,
            uint256[] memory operatorRatiosToDelegate,
            uint256 totalRatio
        )

Calculate how totalBuffered should be delegated between the active validators, depending on if the system is balanced or not:

  • If the system is balanced - returns a list of validators with zeros.

  • If the system is not balanced - returns a list of validators, a list of shares and the total amount of shares, how to delegate these shares to validators.

Will return an error if:

  • the number of validators on the contract is 0

  • there are no validators with the ACTIVE status and with ValidatorShare.delegation() == TRUE

  • validators with EJECTED or UNSTAKED status are present

getValidatorsRebalanceAmount

function getValidatorsRebalanceAmount(uint256 _amountToReDelegate)
        external
        view
        returns (
            ValidatorData[] memory validators,
            uint256[] memory operatorRatiosToRebalance,
            uint256 totalRatio,
            uint256 totalToWithdraw
        )

Calculate how the system could be rebalanced depending on the current buffered tokens. This function utilizes MAX_WITHDRAW_PERCENTAGE_PER_REBALANCE.

If the system is not balanced, it will return

  • a list of validators

  • how many shares to withdraw from them

  • the sum of shares

  • the amount to withdraw that corresponds to the sum of shares.

Will return an error if:

  • the number of validators on the contract is < 2

  • there are no validators with the ACTIVE status and with ValidatorShare.delegation() = TRUE

  • validators with EJECTED or UNSTAKED statuses are present

  • the system is balanced

  • totalToWithdraw value is 0 (if there is no need to transfer funds between validators to achieve the balanced state).

getValidatorsRequestWithdraw

function _getValidatorsRequestWithdraw()
        private
        view
        returns (
            ValidatorData[] memory activeValidators,
            uint256[] memory stakePerOperator,
            uint256 totalDelegated,
            uint256 minAmount,
            uint256 maxAmount
        )

Depending on whether the system is balanced and the amount to withdraw, it returns a list of validators the system can withdraw from:

  • list of validators

  • how much is delegated

  • IDs of those who have delegated more than the average

  • IDs of those who have delegated less than average

  • how much can be withdrawn from each validator if the system is not balanced

  • how many validators can be withdrawn from if the system is balanced

getNodeOperator

function getNodeOperator(uint256 _validatorId)
        external
        view
        returns (FullNodeOperatorRegistry memory nodeOperator)

Returns the validator information by ID.

getNodeOperator

function getNodeOperator(address _rewardAddress)
        external
        view
        returns (FullNodeOperatorRegistry memory nodeOperator)

Returns the validator information by rewardAddress.

getNodeOperatorStatus

 function getNodeOperatorStatus(uint256 _validatorId)
        external
        view
        returns (NodeOperatorRegistryStatus operatorStatus)

Returns operator status according to ID.

getValidatorIds

function getValidatorIds()
        external
        view
        returns (uint256[] memory)

Return a list of all validator ids in the system.

getProtocolStats

function getProtocolStats()
        external
        view
        returns (
            bool isBalanced,
            uint256 distanceMinMaxStake,
            uint256 minAmount,
            uint256 maxAmount
        )

Return the statistics about the protocol as a list:

  • whether the system is balanced

  • the distance between the maximum and minimum validator stake

  • minimum validator stake

  • maximum validator stake

getStats

 function getStats()
        external
        view
        returns (
            uint256 inactiveNodeOperator,
            uint256 activeNodeOperator,
            uint256 jailedNodeOperator,
            uint256 ejectedNodeOperator,
            uint256 unstakedNodeOperator
        )

Returns the number of operators for each status.

Methods

initialize

function initialize(
        IStakeManager _stakeManager,
        IKnBONE _knBONE,
        address _dao
    ) external initializer

Initializer function, not called after initialization.

exitNodeOperatorRegistry

function exitNodeOperatorRegistry() external nonReentrant

Exit the node operator registry. ONLY the owner of the node operator can call this function.

removeInvalidNodeOperator

function removeInvalidNodeOperator(uint256 _validatorId)
        external
        whenNotPaused
        nonReentrant

Remove a node operator from the system if the Node Operator is either Unstaked or Ejected.

setRewardAddress

function setRewardAddress(address _newRewardAddress)
        external
        whenNotPaused

Update the reward address of a Node Operator. ONLY Operator owner can call this function

Admin Methods

This method can be called by ADMIN-only roles

pause

function pause() external onlyRole(PAUSE_ROLE) 

Allows an authorized user with PAUSE ROLE to pause the NodeOperatorRegistry contract.

unpause

function unpause() external onlyRole(UNPAUSE_ROLE)

Allows an authorized user with UNPAUSE ROLE to unpause the NodeOperatorRegistry contract.

DAO Methods

These methods can be called by DAO-only roles.

addNodeOperator

function addNodeOperator(uint256 _validatorId, address _rewardAddress)
        external
        onlyRole(ADD_NODE_OPERATOR_ROLE)
        nonReentrant

Add a new node operator to the system.

removeNodeOperator

function removeNodeOperator(uint256 _validatorId)
        external
        onlyRole(REMOVE_NODE_OPERATOR_ROLE)
        nonReentrant

Remove a node operator from the system and withdraw total delegated tokens to it. ONLY DAO can execute this function.

setKnBONEAddress

function setKnBONEAddress(address _newKnBONE)
        external
        onlyRole(DAO_ROLE)

Allows the DAO to set the knBONE contract address.

setDistanceThreshold

function setDistanceThreshold(uint256 _newDistanceThreshold)
        external
        onlyRole(DAO_ROLE)

Allows the DAO to set the distance threshold for balancing the system.

setMinRequestWithdrawRange

function setMinRequestWithdrawRange(
        uint8 _newMinRequestWithdrawRangePercents
    ) external onlyRole(DAO_ROLE)

Allows the DAO to set the minimum request withdraw range to keep the system balanced

setMaxWithdrawPercentagePerRebalance

setMaxWithdrawPercentagePerRebalance(
        uint256 _newMaxWithdrawPercentagePerRebalance
    ) external onlyRole(DAO_ROLE)

Allows the DAO to set the maximum withdraw percentage per balance of each validator

Last updated