logoDeveloper Hub

Validator Manager Contract

Documentation for the Validator Manager used to manage Avalanche L1 validators, as defined in ACP-77.

The contracts for ValidatorManager can be found in the Teleporter repository here

Overview

The ValidatorManager contract is used to create and operate a sovereign L1. When converting to a sovereign L1, the address of the ValidatorManager contract is specified in the ConvertSubnetToL1Tx transaction to the P-Chain. The ValidatorManager contract is then initialized with the initial validator set, and the subnet is converted to an L1 with the initial validators registered to the P-Chain.

All operations to adding and removing validators after ConvertSubnetToL1Tx must be processed through the ValidatorManager contract. If the ValidatorManager contract is upgradeable, and can be replaced with a new implementation contract if necessary.

Deploying

Three concrete ValidatorManager contracts are provided - PoAValidatorManager, NativeTokenStakingManager, and ERC20TokenStakingManager. NativeTokenStakingManager and ERC20TokenStakingManager implement PoSValidatorManager, which itself implements ValidatorManager. These are implemented as upgradeable contracts. There are numerous guides for deploying upgradeable smart contracts, but the general steps are as follows:

Deploy the implementation contract

Deploy the proxy contract

Call the implementation contract's initialize function

  • Each flavor of ValidatorManager requires different settings. For example, ValidatorManagerSettings specifies the churn parameters, while PoSValidatorManagerSettings specifies the staking and rewards parameters.

Initialize the validator set by calling initializeValidatorSet

  • When a Subnet is first created on the P-Chain, it must be explicitly converted to an L1 via ConvertSubnetToL1Tx. The resulting SubnetToL1ConversionMessage Warp message is provided in the call to initializeValidatorSet to specify the starting validator set in the ValidatorManager. Regardless of the implementation, these initial validators are treated as PoA and are not eligible for staking rewards.

PoAValidatorManager

Proof-of-Authority validator management is provided via PoAValidatorManager, which restricts modification of the validator set to a specified owner address. After deploying PoAValidatorManager.sol and a proxy, the initialize function takes the owner address, in addition to standard ValidatorManagerSettings.

PoSValidatorManager

Proof-of-Stake validator management is provided by the abstract contract PoSValidatorManager, which has two concrete implementations: NativeTokenStakingManager and ERC20TokenStakingManager. In addition to basic validator management provided in ValidatorManager, PoSValidatorManager supports uptime-based validation rewards, as well as delegation to a chosen validator. This state transition diagram illustrates the relationship between validators and delegators.

The weightToValueFactor fields of the PoSValidatorManagerSettings passed to PoSValidatorManager's initialize function sets the factor used to convert between the weight that the validator is registered with on the P-Chain, and the value transferred to the contract as stake. This involves integer division, which may result in loss of precision. When selecting weightToValueFactor, it's important to make the following considerations:

  1. If weightToValueFactor is near the denomination of the asset, then staking amounts on the order of 1 unit of the asset may cause the converted weight to round down to 0. This may impose a larger-than-expected minimum stake amount.
    • Ex: If USDC (denomination of 6) is used as the staking token and weightToValueFactor is 1e9, then any amount less than 1,000 USDC will round down to 0 and therefore be invalid.
  2. Staked amounts up to weightToValueFactor - 1 may be lost in the contract as dust, as the validator's registered weight is used to calculate the original staked amount.
    • Ex: value=1001 and weightToValueFactor=1e3. The resulting weight will be 1. Converting the weight back to a value results in value=1000.
  3. The validator's weight is represented on the P-Chain as a uint64. PoSValidatorManager restricts values such that the calculated weight does not exceed the maximum value for that type.

NativeTokenStakingManager

NativeTokenStakingManager allows permissionless addition and removal of validators that post the L1's native token as stake. Staking rewards are minted via the Native Minter Precompile, which is configured with a set of addresses with minting privileges. As such, the address that NativeTokenStakingManager is deployed to must be added as an admin to the precompile. This can be done by either calling the precompile's setAdmin method from an admin address, or setting the address in the Native Minter precompile settings in the chain's genesis (config.contractNativeMinterConfig.adminAddresses). There are a couple of methods to get this address: one is to calculate the resulting deployed address based on the deployer's address and account nonce: keccak256(rlp.encode(address, nonce)). The second method involves manually placing the NativeTokenStakingManager bytecode at a particular address in the genesis, then setting that address as an admin.

{
    "config" : {
        ...
        "contractNativeMinterConfig": {
            "blockTimestamp": 0,
            "adminAddresses": [
                "0xffffffffffffffffffffffffffffffffffffffff"
            ]
        }
    },
    "alloc": {
        "0xffffffffffffffffffffffffffffffffffffffff": {
            "balance": "0x0",
            "code": "<NativeTokenStakingManagerByteCode>",
            "nonce": 1
        }
    }
}

ERC20TokenStakingManager

ERC20TokenStakingManager allows permissionless addition and removal of validators that post an ERC20 token as stake. The ERC20 is specified in the call to initialize, and must implement IERC20Mintable. Care should be taken to enforce that only authorized users are able to mint the ERC20 staking token.

Last updated on

On this page

Edit on Github