Proof of Stake

Learn about the Staking Manager.

Proof of Stake (PoS) is a consensus mechanism that allows users to participate in network consensus by staking tokens. The StakingManager contract implements a PoS system that enables validator registration, delegation, and reward distribution based on stake and performance.

The StakingManager Contract

The StakingManager contract is the core implementation that handles validator registration, delegation, and reward distribution. It extends functionality from the ValidatorManager to provide staking-specific operations.

In order to use the StakingManager, you must first deploy the ValidatorManager contract then transfer ownership to the StakingManager.

Key Features

  • Validator Registration: Users can lock tokens to become validators
  • Delegation: Token holders can delegate their stake to validators
  • Reward Distribution: Automated calculation and distribution of staking rewards
  • Uptime Tracking: Monitoring validator performance for reward adjustments

The StakingManager builds upon the ValidatorManager contract, inheriting all validation functionality while adding staking-specific features.

StakingManager Implementations

The system provides two different implementations of the StakingManager contract to accommodate different token types:

ERC20TokenStakingManager

The ERC20TokenStakingManager allows staking using standard ERC20 tokens. This implementation:

  • Accepts any ERC20 token that implements the IERC20Mintable interface
  • Uses ERC20 transfer mechanisms for locking and unlocking stake
  • Mints new tokens when distributing rewards
function initiateValidatorRegistration(
    bytes memory nodeID,
    bytes memory blsPublicKey,
    uint64 registrationExpiry,
    PChainOwner memory remainingBalanceOwner,
    PChainOwner memory disableOwner,
    uint16 delegationFeeBips,
    uint64 minStakeDuration,
    uint256 stakeAmount
) external nonReentrant returns (bytes32 validationID)

NativeTokenStakingManager

The NativeTokenStakingManager allows staking using the blockchain's native token. This implementation:

  • Accepts native currency (e.g., AVAX) via payable functions
  • Uses direct value transfers for locking and unlocking stake
  • Leverages the INativeMinter precompile to mint rewards
function initiateValidatorRegistration(
    bytes memory nodeID,
    bytes memory blsPublicKey,
    uint64 registrationExpiry,
    PChainOwner memory remainingBalanceOwner,
    PChainOwner memory disableOwner,
    uint16 delegationFeeBips,
    uint64 minStakeDuration
) external payable nonReentrant returns (bytes32)

Both implementations follow the same core staking logic but differ in how they handle token operations. Choose the appropriate implementation based on whether you want to stake with ERC20 tokens or native currency.

Staking Parameters

The StakingManager is configured with several parameters that govern the staking process:

ParameterDescription
minimumStakeAmountMinimum tokens required to become a validator
maximumStakeAmountMaximum tokens allowed for a single validator
minimumStakeDurationMinimum duration a validator must be staked (must be at least one churn period)
minimumDelegationFeeBipsMinimum delegation fee percentage in basis points
maximumStakeMultiplierMultiplier determining maximum stake a validator can have with delegations
weightToValueFactorConversion factor between validator weight and token value

Validator Lifecycle

The lifecycle of a validator in a PoS system follows a defined path from registration to removal:

Registration

Validators are registered through a two-step process:

function initiateValidatorRegistration(
    bytes memory nodeID,
    bytes memory blsPublicKey,
    uint64 registrationExpiry,
    PChainOwner memory remainingBalanceOwner,
    PChainOwner memory disableOwner,
    uint64 delegationFeeBips,
    uint64 minimumStakeDuration,
    uint256 stakeAmount
) public returns (bytes32)
  1. Initiate Registration: The user calls a registration function with:

    • Node ID and BLS public key
    • Registration expiration timestamp
    • Delegation fee and minimum stake duration
    • Stake amount (must be within min/max bounds)
  2. Complete Registration: Once the registration is confirmed via a Warp message, the validator becomes active.

function completeValidatorRegistration(
    uint32 messageIndex
) public

Validation Period

During the validation period, validators:

  • Maintain network consensus
  • Submit uptime proofs via Warp messages
  • Earn rewards based on stake amount and uptime

Removal

Validators can end their validation period through:

function initiateValidatorRemoval(
    bytes32 validationID
) public
  1. Initiate Removal: The validator owner initiates the removal process
  2. Complete Removal: After confirmation via a Warp message, rewards are distributed and stake is unlocked
function completeValidatorRemoval(
    bytes32 validationID,
    uint32 messageIndex
) public

Delegation

Token holders can delegate their tokens to validators to earn a portion of the rewards without running validator infrastructure.

Delegation Process

For ERC20 Token Staking:

function initiateDelegatorRegistration(
    bytes32 validationID,
    uint256 delegationAmount
) external nonReentrant returns (bytes32)

For Native Token Staking:

function initiateDelegatorRegistration(
    bytes32 validationID
) external payable nonReentrant returns (bytes32)
  1. Initiate Delegation: A user locks tokens to delegate to a specific validator
  2. Complete Delegation: After confirmation, the delegation becomes active
  3. End Delegation: Either the delegator or validator can initiate the removal of delegation
  4. Complete Delegation Removal: After confirmation, rewards are distributed and delegated tokens are unlocked

Delegation Rewards

Rewards are split between validators and delegators:

  • Delegators receive rewards proportional to their stake and the validator's uptime
  • Validators receive a fee from delegator rewards (set as delegationFeeBips)

Setting delegation fees too high may discourage delegators, while setting them too low may not provide enough incentive for running validator infrastructure.

Uptime Tracking

Validator uptime is crucial for reward calculation:

  • Validators submit uptime proofs through Warp messages
  • Proofs are verified and stored in the contract
  • Higher uptime leads to higher rewards for both validators and delegators
function submitUptimeProof(
    bytes32 validationID,
    uint64 timestamp
) public

Reward Calculation

Rewards are calculated based on:

  • Stake amount
  • Duration of staking
  • Validator uptime
  • A configurable reward calculation formula

The reward formula uses the following basic structure:

rewards = (stakeAmount * rewardBasisPoints * stakeDuration) / (SECONDS_IN_YEAR * BIPS_CONVERSION_FACTOR)
where:
- stakeDuration = stakingEndTime - stakingStartTime
- rewards = 0 if uptimeSeconds * 100 < stakeDuration * 80

It is defined in a separate contract called the RewardCalculator.

function calculateReward(
    uint256 stakeAmount,
    uint64 validatorStartTime,
    uint64 stakingStartTime,
    uint64 stakingEndTime,
    uint64 uptimeSeconds
) external view returns (uint256)

Security Considerations

The StakingManager implements several security features to protect the network and its participants:

  • Reentrancy Guards: All functions that modify state are protected by reentrancy locks
  • Input Validation: Rigorous validation of all inputs including:
    • Node ID and BLS public key formats
    • Stake amounts within min/max bounds
    • Delegation fee percentages within allowable ranges
    • Duration periods meeting minimum requirements
  • Timelock Mechanisms: Minimum stake durations to prevent churn abuse and network instability
  • Authority Checks: Validation to ensure only authorized parties can perform specific actions:
    • Only validator owners can remove their validators
    • Only delegators (or validators) can remove delegations
    • Only valid validators can submit uptime proofs
  • Slashing Conditions: Protection against validator misbehavior through reduced rewards

The security measures implemented in the StakingManager aim to balance network security with user flexibility.

Technical Integration

Developers integrating with the Staking Manager should understand several key technical aspects:

Weight to Value Conversion

The contract uses a conversion between token value and validator weight:

function _calculateValidatorWeight(uint256 amount) internal view returns (uint64) {
    return uint64(amount / weightToValueFactor);
}

Warp Messaging System

Warp messages are used for cross-chain communication between the C-Chain and P-Chain:

IWarpMessenger public constant WARP_MESSENGER =
    IWarpMessenger(0x0200000000000000000000000000000000000005);

Two-Phase Operations

All state-changing operations follow a two-phase commit pattern:

  1. Initiation Phase: Records intent and initiates cross-chain messaging
  2. Completion Phase: Verifies cross-chain confirmation and finalizes the operation

Validator and Delegation States

The contract maintains detailed state information for validators and delegations:

struct Validator {
    ValidatorStatus status;
    bytes nodeID;
    uint64 startingWeight;
    uint64 sentNonce;
    uint64 receivedNonce;
    uint64 weight;
    uint64 startTime;
    uint64 endTime;
    uint64 delegationFeeBips;
    uint64 minimumStakeDuration;
    uint256 stakeAmount;
    address owner;
}
 
struct Delegation {
    DelegationStatus status;
    bytes32 validationID;
    uint256 amount;
    uint64 startTime;
    uint64 endTime;
    address owner;
}

Example: Becoming a Validator

Using ERC20 Token Staking:

// First, approve the StakingManager to spend your tokens
erc20Token.approve(erc20StakingManager, 1000 * 10**18);
 
// 1. Initiate validator registration with 1000 tokens
bytes32 validationID = erc20StakingManager.initiateValidatorRegistration(
    nodeID,
    blsPublicKey,
    block.timestamp + 7 days, // registration expiry
    pChainRemainingBalanceOwner,
    pChainDisableOwner, 
    500, // 5% delegation fee (in basis points)
    60 days, // minimum stake duration
    1000 * 10**18 // stake amount (1000 tokens)
);
 
// 2. Complete registration after receiving confirmation message
erc20StakingManager.completeValidatorRegistration(messageIndex);

Using Native Token Staking:

// 1. Initiate validator registration with 1000 AVAX
bytes32 validationID = nativeStakingManager.initiateValidatorRegistration{value: 1000 * 10**18}(
    nodeID,
    blsPublicKey,
    block.timestamp + 7 days, // registration expiry
    pChainRemainingBalanceOwner,
    pChainDisableOwner, 
    500, // 5% delegation fee (in basis points)
    60 days // minimum stake duration
);
 
// 2. Complete registration after receiving confirmation message
nativeStakingManager.completeValidatorRegistration(messageIndex);

Example: Delegating to a Validator

Using ERC20 Token Staking:

// First, approve the StakingManager to spend your tokens
erc20Token.approve(erc20StakingManager, 100 * 10**18);
 
// 1. Initiate delegation with 100 tokens
bytes32 delegationID = erc20StakingManager.initiateDelegatorRegistration(
    validationID,
    100 * 10**18 // delegation amount (100 tokens)
);
 
// 2. Complete delegation after receiving confirmation message
erc20StakingManager.completeDelegatorRegistration(delegationID, messageIndex);

Using Native Token Staking:

// 1. Initiate delegation with 100 AVAX
bytes32 delegationID = nativeStakingManager.initiateDelegatorRegistration{value: 100 * 10**18}(
    validationID
);
 
// 2. Complete delegation after receiving confirmation message
nativeStakingManager.completeDelegatorRegistration(delegationID, messageIndex);

Events

The StakingManager contract emits several events to track staking and delegation activities:

  • InitiatedValidatorStake: Emitted when a new validator stake is initiated
  • CompletedValidatorStake: Emitted when a validator stake is completed
  • InitiatedDelegation: Emitted when a new delegation is initiated
  • CompletedDelegation: Emitted when a delegation is completed
  • SubmittedUptimeProof: Emitted when a validator submits an uptime proof
  • DistributedRewards: Emitted when rewards are distributed

These events can be monitored by network participants to track rewards and stake activities.

Implementation-Specific Considerations

ERC20 Token Implementation

  • Token Requirements: The ERC20 token must implement the IERC20Mintable interface
  • Token Approval: Users must approve the StakingManager contract to spend their tokens before staking or delegating
  • Reward Minting: New tokens are minted when distributing rewards using the token's mint function

Native Token Implementation

  • Native Currency: Stake and delegation amounts are sent directly with transactions using the msg.value field
  • Native Minting: Rewards are distributed by calling the INativeMinter precompile at address 0x0200000000000000000000000000000000000001
  • Value Transfer: Native tokens are unlocked by sending them directly to recipient addresses

Best Practices

  • Monitor validator uptime regularly
  • Set reasonable delegation fees to attract delegators
  • Consider the lock-up period when staking or delegating
  • Use the reward recipient functionality for better fund management
  • Maintain sufficient reserve funds for gas fees required for submitting uptime proofs
  • When using ERC20 tokens, ensure sufficient token approval before staking or delegating
  • Choose the appropriate StakingManager implementation based on your token type (ERC20 or native)

Is this guide helpful?