Skip to content

Voting Power

The VotingPowerProvider is the Relay contract that turns delegated Symbiotic stake into operator voting power inside a validator set. It sits between Symbiotic Core (vaults, operators, networks) and the Relay settlement layer, and exposes a clean interface for:

  • which operators are in the validator set
  • how much voting power each one has
  • what that power was at a specific timestamp (for verifying old decisions)

This is what Relay uses to build validator sets and what Settlement uses to verify signatures.

Inputs

At a high level, VotingPowerProvider pulls three kinds of data:

  • Stake and vault state from Symbiotic Core (how much collateral each operator has in which vaults / networks).
  • Onboarding / filtering modules, e.g.
    • OperatorsWhitelist / OperatorsBlacklist / OperatorsJail – which operators are even allowed in the set
    • SharedVaults / OperatorVaults – which vaults are considered for this network
    • MultiToken / OpNetVaultAutoDeploy – which tokens and “auto-created” vaults are in scope
  • Voting power calculators, which define how raw stake → voting power.

The public view methods (like getOperatorVotingPower and getOperatorVotingPowerAt) are what off-chain tooling and Relay’s ValSetDriver actually call when they derive the active set and its weights.

From Delegated Stake to Voting Power

The core idea: delegated stake is the input, but the contract lets you pick the function that maps “stake” to “voting power”.

Some standard derivations:

  • Equal – every opted-in operator gets the same power, regardless of stake. Useful for “one node, one vote” or PoA-style governance.
  • Linear – power proportional to stake. This is the vanilla PoS model: double the effective stake, double the voting power.
  • Capped / concave – diminishing returns at higher stake. You can cap per-operator power or use a concave function to stop one operator from dominating the set even if they bring a lot of collateral.
  • Behavior-aware – adjust weights based on uptime, freshness, or custom performance metrics. In practice you’d compute a “score” off-chain and feed it in via weights or parameters that sit on top of the base calculators.

VotingPowerProvider achieves this through a plug-in set of VotingPowerCalculators. The repo ships with several that you can compose or chain:

  • EqualStakeVPCalc
  • NormalizedTokenDecimalsVPCalc – normalize all tokens to 18 decimals
  • PricedTokensChainlinkVPCalc – convert different tokens to a common value using Chainlink price feeds
  • WeightedTokensVPCalc – apply token-level weights
  • WeightedVaultsVPCalc – apply vault-level weights (e.g. “this vault counts 1.2×, that one 0.8×”)

Networks can pick a simple single calculator (e.g. “linear stake using normalized decimals”) or a pipeline (“normalize decimals → price everything in ETH → apply vault weights”).

Multi-Token Considerations

In practice, stake can come from multiple collaterals across several vaults:

  • LST A, LRT B, native token, etc.
  • different decimals and potentially different prices

VotingPowerProvider does not impose one global rule here; it just provides building blocks. The network defines:

  • which tokens are accepted (via MultiToken module and supported-token registration)
  • how each token is converted into a single comparable number (e.g. price feeds, fixed haircuts, or simple 1:1)
  • whether some tokens or vaults get higher or lower weight in the final voting power

The end result is a single voting power number per operator that already bakes in all these decisions.

Quorum and Time Variation

Once voting power is defined, the network can reason about quorums and thresholds:

  • decisions might require > 50% or ≥ 2/3 of total voting power
  • some sub-protocols (e.g. light client, DA, bridge) can have their own minimum voting power requirements

Voting power is time-varying:

  • stake moves in and out of vaults
  • operators join, leave, or are jailed / unregistered
  • token prices change if you use price-based calculators
  • off-chain behavior metrics change over time

To keep things tractable, most networks update the validator set once per epoch and use that snapshot for all decisions in that period. Relay’s contracts are built around this:

  • ValSetDriver uses VotingPowerProvider to derive the validator set at a chosen genesis and at subsequent epochs.
  • Settlement verifies signatures against exactly that compressed validator set header, so verifiers and networks agree on which power distribution applied.

This gives you a clean, epoch-by-epoch history of “who had how much power when”.

With and Without Relay

You can think of VotingPowerProvider as the Relay version of what a custom middleware might do.

  • Without Relay Your own middleware reads vault accounting and Delegator state directly from Symbiotic Core, applies your stake → voting power rules off-chain, and uses that to drive your protocol. You still need to handle cross-chain verification and efficient proof formats yourself.
  • With Relay VotingPowerProvider is the on-chain oracle of voting power for the Relay network. Relay’s off-chain sidecar and ValSetDriver:
    • read operator/vault data from Symbiotic Core
    • call VotingPowerProvider to get per-operator voting power
    • compress that into a validator set header
    • commit it into Settlement, which is then used to verify aggregated signatures on any connected chain

In other words: VotingPowerProvider is where you define what “power” means for your network, and Relay takes that definition and turns it into a cheap, verifiable validator set you can reuse everywhere.