Skip to content

Rewards

Read Learn first

Rewards is a encouraging mechanism provided by a network to motivate operators to do their work. The rewards are divided into Staker and Operator rewards:

  • The Staker rewards can be provided depending purely on the provided stake’s value (the representation of value can differ from the standard "dollars view" depending on alignment, token distribution, or other factors)
  • The Operator rewards can be provided depending on the economic factors similar to the above ones and on the operators’ performance metrics (e.g., number of tasks completed, liveness, or validators’ distribution).

Staker Rewards

The rewards contracts are not a part of the core contracts. However, we provide a default implementation of Staker rewards, which is represented by a simple pro-rata rewards distribution concerning the collateral amounts provided by stakers at the capture time point and providing an ability to the Vault curator to receive fees.

Code: DefaultStakerRewards

Factory deployment: DefaultStakerRewardsFactory

Counterparties

  • Network:

    1. Distributes the rewards via distributeRewards()
  • Staker:

    1. Claims the rewards via claimRewards()
  • Curator:

    1. Configures an admin fee via setAdminFee()
    2. Claims an admin fee (it accumulates after each distribution) via claimAdminFee()

Example

This example walks you through setting up a test environment to simulate the staker rewards distribution process on Holešky.

Prepare Mock Accounts

You'll need 6 EOAs:

  • Network Admin: registers the network
  • Middleware: set by the network to handle reward distribution and vault opt-ins
  • Vault Admin: sets up the vault and allocates shares
  • Mock Operator: receives delegated stake
  • Two Test Stakers: deposit collateral

Register Network & Set Middleware

  1. Register your network
  2. Set middleware address

Create Vault & Register Operator

  1. Create a vault using wstETH as collateral.
    • Sample Vault address: 0x0351bE785c8Abfc81e8d8C2b6Ef06A7fc62478a0
    • Creation tx
  2. Register operator in Operator Registry
  3. Opt operator into:
  4. Allocate shares:
  5. Submit 3 PRs with metadata (network, vault, operator) to the metadata-holesky repo. Examples: 787980

Vault Opt-In (Network)

  1. Opt-in to vault by calling setMaxNetworkLimit(0, 1e18)
  2. From emitted event, extract subnetwork ID
  3. Vault Admin: accept opt-in by calling setNetworkLimit

Deploy Rewards Contract

  1. Use DefaultStakerRewardsFactory to create DefaultStakerRewards contract (can be deployed by a Vault Admin or a Network)
  2. Submit rewards metadata PR

Distribute Rewards

Encode data parameter
  • To reproduce manually, use ABI encoder to cook it with:

    • Input types: uint48,uint256,bytes,bytes
    • Input values: 1752766520,1000,0x,0x
      • timestamp - use current timestamp
      • maxAdminShare (10%) - anti-frontrun protection
      • bytes inputs are hints (set to 0x)
  • To reproduce in Solidity:

    DistributeRewards.s.sol
    address rewardsContract = 0x3CBc80E19d558a4012CfE93dD724c1a52ad41EF5;
    address network = 0x0351bE785c8Abfc81e8d8C2b6Ef06A7fc62478a0;
    address token = 0x5FbDB2315678afecb367f032d93F642f64180aa3;
    uint256 amount = 1000000000000000000;
    uint48 timestamp = 1752766520;
    uint256 maxAdminFee = 1000;
    bytes memory activeSharesHint = new bytes(0);
    bytes memory activeStakeHint = new bytes(0);
     
    bytes memory data = abi.encode(timestamp, maxAdminFee, activeSharesHint, activeStakeHint);
    IDefaultStakerRewards(rewardsContract).distributeRewards(network, token, amount, data);

This gives the data parameter:

Expected output
0x00000000000000000000000000000000000000000000000000000000687942a500000000000000000000000000000000000000000000000000000000000003e8000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

rewards-vault.png

User Flow

rewards-claim.png

Operator Rewards

Since the rewards contracts are not a part of the core contracts, we allow users to provide their own implementations. Here are three example implementations:

  1. The network performs off-chain calculations to determine the reward distributions. After calculating the rewards, the network executes batch transfers to distribute the rewards in a consolidated manner.
  2. The network performs off-chain calculations to determine rewards and generates a Merkle tree, allowing operators to claim their rewards.
  3. The network performs on-chain reward calculations within its middleware to determine the distribution of rewards.

We provide a default implementation of the operator rewards, which is represented by a simple Merkle tree rewards distribution, where each leaf represents a recipient (an operator’s treasury address) and the total amount of tokens earned for the whole period.

Code: DefaultOperatorRewards

Factory deployment: DefaultOperatorRewardsFactory

Counterparties

  • Network:
    1. Calculates a Merkle root for the rewards distribution (we’ve implemented a CLI for that purpose)
    2. Distributes the rewards via distributeRewards()
  • Operator:
    1. Claims the rewards via claimRewards() by the usage of a Merkle proof (it can be got with the help of CLI)

Notes

  • In production, middleware logic should be implemented in a contract.
  • This guide uses testnet (Holešky) with simplified EOAs for ease of reproduction.
  • Vault curators are typically responsible for reward contract deployment and share allocations.

Next Steps

Slashing

Learn how Slashing mechanism can be integrated into your system if needed.

Relay On-Chain

Otherwise, proceed to the the development of your protocol's on-chain part using Relay smart contracts.