Skip to main content

Networks

Description

network

In Symbiotic, we define networks as any protocol that requires a decentralized infrastructure network to deliver a service in the crypto economy, e.g. enabling developers to launch decentralized applications by taking care of validating and ordering transactions, providing off-chain data to applications in the crypto economy, or providing users with guarantees about cross-network interactions, etc.

Decentralized infrastructure networks can utilize Symbiotic to flexibly source their security in the form of operators and economic backing. In some cases, protocols may consist of multiple sub-networks with different infrastructure roles. The Symbiotic protocol’s modular design allows developers of such protocols to define the rules of engagement that participants need to opt into for any of these sub-networks.

Technical Overview

In Symbiotic, networks are represented through a network address (either an EOA or a contract) and a middleware contract, which can incorporate custom logic and is required to include slashing logic. The core protocol's fundamental functionalities encompass slashing operators and rewarding both stakers and operators.

Epoch

A network epoch (let's name it NETWORK_EPOCH\text{NETWORK\_EPOCH}) is a period while a certain operator set, obtained given the captured stake, operates for the good of the network. The epoch plus the vault's veto and execute phases' durations should not exceed the duration of the vault's epoch to ensure that withdrawals do not impact the captured stake (however, the conditions can be softer in practice):

  • CYCLE_DURATION=NETWORK_EPOCH+vetoDuration+executeDuration\text{CYCLE\_DURATION} = \text{NETWORK\_EPOCH} + \text{vetoDuration} + \text{executeDuration}
  • CYCLE_DURATION<=EPOCH\text{CYCLE\_DURATION} <= \text{EPOCH}

See Vault Accounting Details.

See Vault Epoch Details.

Staking

The vault allocates stakes by setting limits for network-resolver and operator-network pairs.

See Limits Details.

Given the current active\text{active} balance of the vault and the limits, we can capture the stake for the subsequent network epoch:

networkStake=min(activeSupply,resolver{min(networkResolverLimit,networkResolverLimitIn(CYCLE_DURATION))},operator{min(operatorNetworkLimit,operatorNetworkLimitIn(CYCLE_DURATION))})\text{networkStake} = \min \left( \begin{array}{l} \text{activeSupply}, \\ \sum_{\text{resolver}} \left\{ \min \left( \begin{array}{l} \text{networkResolverLimit}, \\ \text{networkResolverLimitIn}(\text{CYCLE\_DURATION}) \end{array} \right) \right\}, \\ \sum_{\text{operator}} \left\{ \min \left( \begin{array}{l} \text{operatorNetworkLimit}, \\ \text{operatorNetworkLimitIn}(\text{CYCLE\_DURATION}) \end{array} \right) \right\} \end{array} \right)

Limits

The limits are set in the vault, and the network cannot control this process (unless the vault is managed by the network). However, the realization prevents the vault from removing the previously given slashing guarantees.

See Limits Details.

Moreover, the network can limit the maximum amount of stake it wants to use via the Vault.setMaxNetworkResolverLimit() method.

See Network Resolver Limit Details.

Staking Lifecycle:

  1. The network registers by calling NetworkRegistry.registerNetwork()
  2. The network must opt into the vault by invoking NetworkOptInService.optIn(resolver, vault).
  3. Operators must opt into the vault and the network.
  4. Stakers deposit funds into the vault.
  5. The network sets a maximum stake amount for the vault by calling Vault.setMaxNetworkResolverLimit(resolver, amount).
  6. The NETWORK_RESOLVER_LIMIT_SET_ROLE holder defines the stake limit for the network-resolver pair.
  7. The OPERATOR_NETWORK_LIMIT_SET_ROLE holder defines the stake limit for the operator-network pair.

The current stake amount cannot be withdrawn for at least one epoch, although this restriction does not apply to cross-slashing.

Operator Set

The network has the flexibility to configure the operator set within the middleware or network contract.

The following functions could be useful:

  • Vault.operatorNetworkLimit(operator, network) or Vault.minStakeDuring(network, resolver, operator, duration): Determines minimum stake eligibility. Note that the sum of operators' stakes may exceed the network's total stake, depending on the network's and operators' limits in the vault.
  • OperatorOptInService.wasOptedInAfter(operator, network, timestamp): Checks the opt-in status.

Slashing

See Slashing Details.

slashableAmount(resolver,operator)=min(totalSupply,networkResolverLimit,operatorNetworkLimit) \text{slashableAmount}(\text{resolver}, \text{operator}) = \min(\text{totalSupply}, \text{networkResolverLimit}, \text{operatorNetworkLimit})

It can be obtained by invoking the function Vault.slashableAmount(). The limits operate as described above.

Note that the actual slashed amount may be less than the requested one. This is influenced by updating the network-resolver and operator-network limits, as well as by the cross-slashing. Limits can be updated between the time of the slash request and its execution (i.e., requestTime+vetoDuration\text{requestTime} + \text{vetoDuration} and requestTime+vetoDuration+executeDuration\text{requestTime} + \text{vetoDuration} + \text{executeDuration}). These limit updates can be observed in Vault.nextNetworkResolverLimit() and Vault.nextOperatorNetworkLimit().

The network cannot slash if it is not opted-in. Also, it is important to note that an operator cannot be slashed if he was not opted-in before the start of the previous epoch.

Rewards

In Symbiotic, rewards are categorized into:

  • Operator rewards
  • Staker rewards

Operator Rewards

The network distributes the operator rewards at its discretion. Here are three examples:

  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.
Source of Data for Network On-Chain Reward Calculations

For each epoch, networks obtain their staking information through our system. Additionally, all operators register through the network, providing necessary details such as commission rates, fixed payments, and other relevant conditions. This registration process ensures that networks have the required data to perform accurate on-chain reward calculations in their middleware.

Staker Rewards

See Staker Rewards Details.

API reference

NetworkRegistry Interface

interface INetworkRegistry {
/**
* @notice Emitted when an entity is added.
* @param entity address of the added entity
*/
event AddEntity(address indexed entity);

/**
* @notice Get if a given address is an entity.
* @param account address to check
* @return if the given address is an entity
*/
function isEntity(address account) external view returns (bool);

/**
* @notice Get a total number of entities.
* @return total number of entities added
*/
function totalEntities() external view returns (uint256);

/**
* @notice Get an entity given its index.
* @param index index of the entity to get
* @return address of the entity
*/
function entity(uint256 index) external view returns (address);

/**
* @notice Register the caller as a network.
*/
function registerNetwork() external;
}

NetworkMiddlewareService Interface

interface INetworkMiddlewareService {
/**
* @notice Emitted when a middleware is set for a network.
* @param network address of the network
* @param middleware new middleware of the network
*/
event SetMiddleware(address indexed network, address middleware);

/**
* @notice Get the network registry's address.
* @return address of the network registry
*/
function NETWORK_REGISTRY() external view returns (address);

/**
* @notice Get a given network's middleware.
* @param network address of the network
* @return middleware of the network
*/
function middleware(address network) external view returns (address);

/**
* @notice Set a new middleware for a calling network.
* @param middleware new middleware of the network
*/
function setMiddleware(address middleware) external;
}

NetworkOptInService Interface

interface INetworkOptInService {
/**
* @notice Emitted when a network opts into a vault.
* @param network address of the network
* @param resolver address of the resolver
* @param vault address of the vault
*/
event OptIn(address indexed network, address indexed resolver, address indexed vault);

/**
* @notice Emitted when a network opts out from a vault.
* @param network address of the network
* @param resolver address of the resolver
* @param vault address of the vault
*/
event OptOut(address indexed network, address indexed resolver, address indexed vault);

/**
* @notice Get the network registry's address.
* @return address of the network registry
*/
function NETWORK_REGISTRY() external view returns (address);

/**
* @notice Get the vault registry's address.
* @return address of the vault registry
*/
function VAULT_REGISTRY() external view returns (address);

/**
* @notice Check if a given network is opted-in to a particular vault.
* @param network address of the network
* @param resolver address of the resolver
* @param vault address of the vault
*/
function isOptedIn(address network, address resolver, address vault) external view returns (bool);

/**
* @notice Get the timestamp of the last opt-out of a given network from a particular vault.
* @param network address of the network
* @param resolver address of the resolver
* @param vault address of the vault
*/
function lastOptOut(address network, address resolver, address vault) external view returns (uint48);

/**
* @notice Check if a given network was opted-in to a particular vault after a given timestamp (inclusively).
* @param network address of the network
* @param vault address of the vault
* @param timestamp time point to check if the network was opted-in after
*/
function wasOptedInAfter(
address network,
address resolver,
address vault,
uint48 timestamp
) external view returns (bool);

/**
* @notice Opt-in a calling network to a particular vault.
* @param resolver address of the resolver
* @param vault address of the vault
*/
function optIn(address resolver, address vault) external;

/**
* @notice Opt-out a calling network from a particular vault.
* @param resolver address of the resolver
* @param vault address of the vault
*/
function optOut(address resolver, address vault) external;
}