---

## /integrate/networks/helpful-core-contracts-endpoints

---
description: "This page lists useful functions for networks across Symbiotic Core contracts."
---

# Helpful Core Contracts' Endpoints

This page lists useful functions for networks across Symbiotic Core contracts.

| Function                                                                                                                                                                                                                                                            | Use-case                                                                                   |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| [`NetworkRegistry.registerNetwork()`](https://github.com/symbioticfi/core/blob/7cb06639c5cd656d1d212dafa2c270b5fde39306/src/interfaces/INetworkRegistry.sol#L12)                                                                                                    | Register network                                                                           |
| [`Vault.isInitialized() → bool`](https://github.com/symbioticfi/core/blob/7cb06639c5cd656d1d212dafa2c270b5fde39306/src/interfaces/vault/IVault.sol#L165)                                                                                                            | Check if the vault has set delegator and slasher before on-boarding as a valid Vault       |
| [`Vault.delegator() → address`](https://github.com/symbioticfi/core/blob/7cb06639c5cd656d1d212dafa2c270b5fde39306/src/interfaces/vault/IVaultStorage.sol#L60)                                                                                                       | Get the Vault's delegator                                                                  |
| [`Vault.slasher() → address`](https://github.com/symbioticfi/core/blob/7cb06639c5cd656d1d212dafa2c270b5fde39306/src/interfaces/vault/IVaultStorage.sol#L72)                                                                                                         | Get the Vault's slasher                                                                    |
| [`BaseDelegator.TYPE() → uint64`](https://github.com/symbioticfi/core/blob/7cb06639c5cd656d1d212dafa2c270b5fde39306/src/interfaces/common/IEntity.sol#L17)                                                                                                          | Get the delegator's type (0 - NetworkRestake, 1 - FullRestake, etc.)                       |
| [`BaseDelegator.setMaxNetworkLimit(uint96 identifier, uint256 amount)`](https://github.com/symbioticfi/core/blob/7cb06639c5cd656d1d212dafa2c270b5fde39306/src/interfaces/delegator/IBaseDelegator.sol#L158)                                                         | Set an amount of collateral you are ready to accept from the Vault (maximum network limit) |
| [`BaseDelegator.maxNetworkLimit(bytes32 subnetwork) → uint256`](https://github.com/symbioticfi/core/blob/7cb06639c5cd656d1d212dafa2c270b5fde39306/src/interfaces/delegator/IBaseDelegator.sol#L125)                                                                 | Check the maximum network limit                                                            |
| [`BaseDelegator.stakeAt(bytes32 subnetwork, address operator, uint48 timestamp, bytes hints) → uint256`](https://github.com/symbioticfi/core/blob/7cb06639c5cd656d1d212dafa2c270b5fde39306/src/interfaces/delegator/IBaseDelegator.sol#L137)                        | Get the operator's stake at the given timestamp                                            |
| [`BaseSlasher.TYPE() → uint64`](https://github.com/symbioticfi/core/blob/7cb06639c5cd656d1d212dafa2c270b5fde39306/src/interfaces/common/IEntity.sol#L17)                                                                                                            | Get the slasher's type (0 - Slasher, 1 - VetoSlasher)                                      |
| [`BaseSlasher.slashableStake() → uint256`](https://github.com/symbioticfi/core/blob/7cb06639c5cd656d1d212dafa2c270b5fde39306/src/interfaces/slasher/IBaseSlasher.sol#L113)                                                                                          | Get the operator's still slashable stake captured at the given timestamp                   |
| [`Slasher.slash() → uint256`](https://github.com/symbioticfi/core/blob/7cb06639c5cd656d1d212dafa2c270b5fde39306/src/interfaces/slasher/ISlasher.sol#L55)                                                                                                            | Slash the operator                                                                         |
| [`VetoSlasher.requestSlash(bytes32 subnetwork, address operator, uint256 amount, uint48 captureTimestamp, bytes hints) -> uint256`](https://github.com/symbioticfi/core/blob/7cb06639c5cd656d1d212dafa2c270b5fde39306/src/interfaces/slasher/IVetoSlasher.sol#L213) | Request slashing of the operator                                                           |
| [`VetoSlasher.executeSlash(uint256 slashIndex, bytes hints) -> uint256`](https://github.com/symbioticfi/core/blob/7cb06639c5cd656d1d212dafa2c270b5fde39306/src/interfaces/slasher/IVetoSlasher.sol#L228)                                                            | Execute slashing of the operator                                                           |
| [`VetoSlasher.setResolver(uint96 identifier, address resolver, bytes hints)`](https://github.com/symbioticfi/core/blob/7cb06639c5cd656d1d212dafa2c270b5fde39306/src/interfaces/slasher/IVetoSlasher.sol#L245)                                                       | Set a resolver                                                                             |
| [`VetoSlasher.resolver(bytes32 subnetwork, bytes hint)`](https://github.com/symbioticfi/core/blob/7cb06639c5cd656d1d212dafa2c270b5fde39306/src/interfaces/slasher/IVetoSlasher.sol#L201)                                                                            | Check the resolver                                                                         |


---

## /integrate/networks

---
description: "This guide walks you through integrating your network with Symbiotic. Follow these steps to register your network, set up security, and start receiving stake..."
---

import { Card1 } from "../../../components/Card1";

# Get Started

This guide walks you through integrating your network with Symbiotic. Follow these steps to register your network, set up security, and start receiving stake allocations.

:::steps
## Network Contract

Register your network's management address in the Symbiotic system.

<div
  style={{
      display: "grid",
      gridTemplateColumns: "repeat(auto-fit, minmax(300px, 1fr))",
      gap: "24px",
      marginBottom: "48px",
  }}
>
  <Card1
    title="Quickstart: Network with Staking"
    description="Get familiar with Protocol: deploy a minimal network, middleware and vault-operator-staker setup"
    icon={
      <svg width="48" height="48" viewBox="0 0 48 48" fill="none">
          <rect
              x="8"
              y="8"
              width="32"
              height="32"
              rx="2"
              stroke="currentColor"
              strokeWidth="2"
              fill="none"
          />
          <rect x="12" y="12" width="24" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="18" width="20" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="24" width="16" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="30" width="18" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
      </svg>
  }
    href="/integrate/networks/minimal-network"
  />

  <Card1
    title="Network Contract"
    description="Deploy production-ready Network contract"
    icon={
      <svg width="48" height="48" viewBox="0 0 48 48" fill="none">
          <rect
              x="8"
              y="8"
              width="32"
              height="32"
              rx="2"
              stroke="currentColor"
              strokeWidth="2"
              fill="none"
          />
          <rect x="12" y="12" width="24" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="18" width="20" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="24" width="16" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="30" width="18" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
      </svg>
  }
    href="/integrate/networks/network-contract"
  />
</div>

## Submit Metadata

<div
  style={{
      display: "grid",
      gridTemplateColumns: "repeat(auto-fit, minmax(300px, 1fr))",
      gap: "24px",
      marginBottom: "48px",
  }}
>
  <Card1
    title=""
    description="Submit a PR with your network's metadata to make it visible on the Symbiotic UI."
    icon={
      <svg width="48" height="48" viewBox="0 0 48 48" fill="none">
          <rect
              x="8"
              y="8"
              width="32"
              height="32"
              rx="2"
              stroke="currentColor"
              strokeWidth="2"
              fill="none"
          />
          <rect x="12" y="12" width="24" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="18" width="20" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="24" width="16" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="30" width="18" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
      </svg>
  }
    href="/integrate/networks/submit-metadata"
  />
</div>

## Set Up Pre-deposit Vault (Optional)

If you need a self-curated vault, deploy and configure it.

<div
  style={{
      display: "grid",
      gridTemplateColumns: "repeat(auto-fit, minmax(300px, 1fr))",
      gap: "24px",
      marginBottom: "48px",
  }}
>
  <Card1
    title="Deploy Pre-deposit Vault"
    description="Configure and deploy a self-curated vault for your network."
    icon={
      <svg width="48" height="48" viewBox="0 0 48 48" fill="none">
          <rect
              x="8"
              y="8"
              width="32"
              height="32"
              rx="2"
              stroke="currentColor"
              strokeWidth="2"
              fill="none"
          />
          <rect x="12" y="12" width="24" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="18" width="20" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="24" width="16" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="30" width="18" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
      </svg>
  }
    href="/integrate/curators/deploy-vault"
  />

  <Card1
    title="Submit Vault's Metadata"
    description="Submit your vault's metadata after deployment."
    icon={
      <svg width="48" height="48" viewBox="0 0 48 48" fill="none">
          <rect
              x="8"
              y="8"
              width="32"
              height="32"
              rx="2"
              stroke="currentColor"
              strokeWidth="2"
              fill="none"
          />
          <rect x="12" y="12" width="24" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="18" width="20" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="24" width="16" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="30" width="18" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
      </svg>
  }
    href="/integrate/curators/submit-metadata"
  />
</div>

## Integrate Rewards

Rewards are essential for healthy protocol economics. Learn how to integrate Symbiotic rewards.

<div
  style={{
      display: "grid",
      gridTemplateColumns: "repeat(auto-fit, minmax(300px, 1fr))",
      gap: "24px",
      marginBottom: "48px",
  }}
>
  <Card1
    title=""
    description="Integrate Symbiotic rewards to distribute rewards to stakers."
    icon={
      <svg width="48" height="48" viewBox="0 0 48 48" fill="none">
          <rect
              x="8"
              y="8"
              width="32"
              height="32"
              rx="2"
              stroke="currentColor"
              strokeWidth="2"
              fill="none"
          />
          <rect x="12" y="12" width="24" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="18" width="20" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="24" width="16" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="30" width="18" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
      </svg>
  }
    href="/integrate/networks/rewards"
  />
</div>

## Integrate Slashing

Slashing protects your protocol by penalizing misbehavior. Learn how slashing works in Symbiotic.

<div
  style={{
      display: "grid",
      gridTemplateColumns: "repeat(auto-fit, minmax(300px, 1fr))",
      gap: "24px",
      marginBottom: "48px",
  }}
>
  <Card1
    title=""
    description="Implement slashing to secure your network and penalize misbehavior."
    icon={
      <svg width="48" height="48" viewBox="0 0 48 48" fill="none">
          <rect
              x="8"
              y="8"
              width="32"
              height="32"
              rx="2"
              stroke="currentColor"
              strokeWidth="2"
              fill="none"
          />
          <rect x="12" y="12" width="24" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="18" width="20" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="24" width="16" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="30" width="18" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
      </svg>
  }
    href="/integrate/networks/slashing"
  />
</div>

## Implement On-Chain Components

Build your on-chain integration using Symbiotic's Relay framework.

<div
  style={{
      display: "grid",
      gridTemplateColumns: "repeat(auto-fit, minmax(300px, 1fr))",
      gap: "24px",
      marginBottom: "48px",
  }}
>
  <Card1
    title="Build & Deploy Relay Contracts"
    description="Build and deploy the four Relay contracts — KeyRegistry, VotingPowerProvider, Settlement, ValSetDriver — then set your network middleware and bring them online."
    icon={
      <svg width="48" height="48" viewBox="0 0 48 48" fill="none">
          <rect
              x="8"
              y="8"
              width="32"
              height="32"
              rx="2"
              stroke="currentColor"
              strokeWidth="2"
              fill="none"
          />
          <rect x="12" y="12" width="24" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="18" width="20" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="24" width="16" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="30" width="18" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
      </svg>
  }
    href="/integrate/networks/relay-onchain"
  />

  <Card1
    title="Set Your Middleware"
    description="Configure your middleware to perform slashings and rewards distributions using the `Network.sol` contract."
    icon={
      <svg width="48" height="48" viewBox="0 0 48 48" fill="none">
          <rect
              x="8"
              y="8"
              width="32"
              height="32"
              rx="2"
              stroke="currentColor"
              strokeWidth="2"
              fill="none"
          />
          <rect x="12" y="12" width="24" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="18" width="20" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="24" width="16" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="30" width="18" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
      </svg>
  }
    href="/integrate/networks/network-contract#manage-your-network"
  />
</div>

## Implement Off-Chain Components

Build your off-chain application using Relay Sidecar.

<div
  style={{
      display: "grid",
      gridTemplateColumns: "repeat(auto-fit, minmax(300px, 1fr))",
      gap: "24px",
      marginBottom: "48px",
  }}
>
  <Card1
    title="Build & Run the Relay Sidecar"
    description="Build and run your off-chain app on the Relay sidecar architecture."
    icon={
      <svg width="48" height="48" viewBox="0 0 48 48" fill="none">
          <rect
              x="8"
              y="8"
              width="32"
              height="32"
              rx="2"
              stroke="currentColor"
              strokeWidth="2"
              fill="none"
          />
          <rect x="12" y="12" width="24" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="18" width="20" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="24" width="16" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="30" width="18" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
      </svg>
  }
    href="/integrate/networks/relay-offchain"
  />
</div>

## Bring the Network Online

Onboard your operators, then commit the genesis validator set. **Order matters:** operators must be registered, keyed, and staked before genesis, or the genesis set is empty.

<div
  style={{
      display: "grid",
      gridTemplateColumns: "repeat(auto-fit, minmax(300px, 1fr))",
      gap: "24px",
      marginBottom: "48px",
  }}
>
  <Card1
    title="Onboard Operators"
    description="Operators register, opt in, register in the VotingPowerProvider with keys, opt into a vault, and stake."
    icon={
      <svg width="48" height="48" viewBox="0 0 48 48" fill="none">
          <rect
              x="8"
              y="8"
              width="32"
              height="32"
              rx="2"
              stroke="currentColor"
              strokeWidth="2"
              fill="none"
          />
          <rect x="12" y="12" width="24" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="18" width="20" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="24" width="16" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="30" width="18" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
      </svg>
  }
    href="/integrate/operators"
  />

  <Card1
    title="Commit Genesis with Relay Utils"
    description="Commit the genesis validator set, register operator keys, and check validator-set status with relay_utils."
    icon={
      <svg width="48" height="48" viewBox="0 0 48 48" fill="none">
          <rect
              x="8"
              y="8"
              width="32"
              height="32"
              rx="2"
              stroke="currentColor"
              strokeWidth="2"
              fill="none"
          />
          <rect x="12" y="12" width="24" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="18" width="20" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="24" width="16" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="30" width="18" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
      </svg>
  }
    href="/integrate/networks/relay-utils"
  />
</div>
:::


---

## /integrate/networks/minimal-network

---
description: "This guide sets up a minimal Hoodi testnet environment so you can validate production flows on testnet."
---

# Quickstart: Network with Staking

This guide sets up a minimal Hoodi testnet environment so you can validate production flows on testnet.

:::info
Although this walkthrough uses externally owned accounts (EOAs) for Network and Middleware, the sequence of on-chain actions outside of the Network and Middleware is the same as if those accounts were production smart contracts.
:::

## Prerequisites

Prepare 6 EOAs for simulation:

- **Network**: network identity account
- **Middleware**: set by the network to handle reward distribution and vault opt-ins
- **Vault Curator**: sets up the vault and allocates shares
- **Operator**: receives delegated stake
- **Two Test Stakers**: deposit collateral

::::steps
### Register Network & Set Middleware

For both transactions, originator (`msg.sender`) is the network address.

1. Register your network in `NetworkRegistry` via `registerNetwork()`.  ([Sample Tx](https://hoodi.etherscan.io/tx/0x2da545af56094c2dbd7e49dfb838915b84e889ea341dae812483ee7c6840c196))
2. Set the middleware address in `NetworkMiddlewareService` via `setMiddleware(middleware)`.  ([Sample Tx](https://hoodi.etherscan.io/tx/0xf57a8e2fe75a9453620c0b2903165a34be9bc20749dc36b966f169e7f8f963ac))

### Create Vault & Register Operator

1. [Create a vault](https://app.hoodi.symbiotic.fi/create) using `wstETH` as collateral.
   - Sample Vault address: `0xb29BAD40B00587dE1145C7862F96746f043b9daD`  ([Sample Tx](https://hoodi.etherscan.io/tx/0x94ad7d76fc41e75528b1d2bbc0496c5664e59900c07f5e8bfa81505cca88d45b))
2. Register operator in `OperatorRegistry` via `registerOperator()`   ([Sample Tx](https://hoodi.etherscan.io/tx/0xc6ff4fabf604325e2357963004f4f96dceefec6e874edbec75677d827fbc507b))
3. Opt operator into Network using `OperatorNetworkOptInService` via `optIn()`  ([Sample Tx](https://hoodi.etherscan.io/tx/0xa116a2e7e7386019147b2952ad59fb169d2043f49088098dfc82c2422ecdd83d))
4. Opt operator into Vault using `OperatorVaultOptInService` via `optIn()`  ([Sample Tx](https://hoodi.etherscan.io/tx/0x81daac1756eb1ca3e5a35f10a356ff7487e0ce1b60fdbf91c05ac11dbfd42eea))
5. Allocate shares in Delegator via `setOperatorNetworkShares()`  ([Sample Tx](https://hoodi.etherscan.io/tx/0xe2ceee91cebadc1c1abf11fbad9b40d32604f00695282cc082a6796bc889376b))
6. (Optional) Submit PRs with metadata (network, vault, operator) to the [metadata-hoodi](https://github.com/symbioticfi/metadata-hoodi/) repo. Examples: [13](https://github.com/symbioticfi/metadata-hoodi/pull/13), [14](https://github.com/symbioticfi/metadata-hoodi/pull/14), [15](https://github.com/symbioticfi/metadata-hoodi/pull/15)

:::info
If you complete step 6, entities and their interactions will be reflected on [app.hoodi.symbiotic.fi](https://app.hoodi.symbiotic.fi).
:::

### Vault Opt-In (Network)

1. Opt-in to vault by calling Delegator via `setMaxNetworkLimit`  ([Sample Tx](https://hoodi.etherscan.io/tx/0x99f6a276c558184a6a5f325a25c1432f10d391d7e26207ffa8aaf1dd9a791efa))
2. From emitted event, extract `subnetwork ID`

Using the **Vault Admin** address: accept opt-in by calling Delegator via `setNetworkLimit`  ([Sample Tx](https://hoodi.etherscan.io/tx/0x3918e61998aff1dc637f15ac3fe9024d6da9f0ef299a40f0b49c12eb623ad249))
::::

When finished, 2 test stakers should be able to deposit to the wstETH vault.


---

## /integrate/networks/network-contract

---
description: "Register your network using the NetworkRegistry smart contract. The registered address serves as your management address for interacting with Symbiotic vaults..."
---

import { Card1 } from "../../../components/Card1";

# Network Contract

Register your network using the `NetworkRegistry` smart contract. The registered address serves as your management address for interacting with Symbiotic vaults to accept stake allocations.

We provide a [network repository](https://github.com/symbioticfi/network) that simplifies registration and makes management more secure. It includes the `Network.sol` contract and tooling to manage it.

The `Network.sol` contract extends [OpenZeppelin](https://www.openzeppelin.com/)'s [`TimelockController.sol`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/governance/TimelockController.sol) with additional functionality to define delays for `(exact target | exact selector)` or `(any target | exact selector)` pairs.

## Prerequisites

:::steps
#### Clone the repository

```bash [bash]
git clone --recurse-submodules https://github.com/symbioticfi/network.git
cd network
```

#### Install dependencies

```bash [bash]
npm install
```
:::

## Deploy Network and Register

The deployment scripts support two scenarios:

1. Deploy a network only (opt into vaults later)
2. Deploy a network with vault opt-ins

:::info
The `Network.sol` contract automatically registers itself on deployment.
:::

### Pure Network Deployment

:::steps
##### Open [`DeployNetwork.s.sol`](https://github.com/symbioticfi/network/blob/main/script/DeployNetwork.s.sol) and configure these settings:

```solidity [DeployNetwork.s.sol]
// Name of the Network
string NAME = "My Network";
// Default minimum delay (will be applied for any action that doesn't have a specific delay yet)
uint256 DEFAULT_MIN_DELAY = 3 days;
// Cold actions delay (a delay that will be applied for major actions like upgradeProxy and setMiddleware)
uint256 COLD_ACTIONS_DELAY = 14 days;
// Hot actions delay (a delay that will be applied for minor actions like setMaxNetworkLimit and setResolver)
uint256 HOT_ACTIONS_DELAY = 0;
// Admin address (will become executor, proposer, and default admin by default)
address ADMIN = 0x0000000000000000000000000000000000000000;

// Optional

// Metadata URI of the Network
string METADATA_URI = "";
// Salt for deterministic deployment
bytes11 SALT = "SymNetwork";
```

##### Edit the configuration fields, then run:

```bash [bash]
forge script script/DeployNetwork.s.sol --rpc-url <RPC_URL> --private-key <PRIVATE_KEY> --etherscan-api-key <ETHERSCAN_API_KEY> --broadcast --verify
```
:::

### Network Deployment With Opt-ins

:::steps
##### Open [`DeployNetworkForVaults.s.sol`](https://github.com/symbioticfi/network/blob/main/script/DeployNetworkForVaults.s.sol) and configure these settings:

```solidity [DeployNetworkForVaults.s.sol]
// Name of the Network
string NAME = "My Network";
// Default minimum delay (will be applied for any action that doesn't have a specific delay yet)
uint256 DEFAULT_MIN_DELAY = 3 days;
// Cold actions delay (a delay that will be applied for major actions like upgradeProxy and setMiddleware)
uint256 COLD_ACTIONS_DELAY = 14 days;
// Hot actions delay (a delay that will be applied for minor actions like setMaxNetworkLimit and setResolver)
uint256 HOT_ACTIONS_DELAY = 0;
// Admin address (will become executor, proposer, and default admin by default)
address ADMIN = 0x0000000000000000000000000000000000000000;
// Vault address to opt-in to (multiple vaults can be set)
address[] VAULTS = [0x0000000000000000000000000000000000000000];
// Maximum amount of delegation that network is ready to receive (multiple vaults can be set)
uint256[] MAX_NETWORK_LIMITS = [0];
// Resolver address (optional, is applied only if VetoSlasher is used) (multiple vaults can be set)
address[] RESOLVERS = [0x0000000000000000000000000000000000000000];

// Optional

// Subnetwork Identifier (multiple subnetworks can be used, e.g., to have different resolvers for the same network)
uint96 SUBNETWORK_ID = 0;
// Metadata URI of the Network
string METADATA_URI = "";
// Salt for deterministic deployment
bytes11 SALT = "SymNetwork";
```

##### Edit the configuration fields, then run:

```bash [bash]
forge script script/DeployNetworkForVaults.s.sol --rpc-url <RPC_URL> --private-key <PRIVATE_KEY> --etherscan-api-key <ETHERSCAN_API_KEY> --broadcast --verify
```
:::

## Manage Your Network

Use these five predefined action scripts:

- [SetMaxNetworkLimit](https://github.com/symbioticfi/network/blob/main/script/actions/SetMaxNetworkLimit.s.sol) - set a new maximum network limit for the vault
- [SetResolver](https://github.com/symbioticfi/network/blob/main/script/actions/SetResolver.s.sol) - set the resolver for the vault (only if the vault uses VetoSlasher)
- [SetMiddleware](https://github.com/symbioticfi/network/blob/main/script/actions/SetMiddleware.s.sol) - set the middleware
- [UpgradeProxy](https://github.com/symbioticfi/network/blob/main/script/actions/UpgradeProxy.s.sol) - upgrade the proxy (network itself)
- [ArbitraryCall](https://github.com/symbioticfi/network/blob/main/script/actions/ArbitraryCall.s.sol) - make a call to any contract with any data

All actions follow a similar pattern. Here's how to use [SetMaxNetworkLimit](https://github.com/symbioticfi/network/blob/main/script/actions/SetMaxNetworkLimit.s.sol) as an example:

:::steps
##### Open [SetMaxNetworkLimit.s.sol](https://github.com/symbioticfi/network/blob/main/script/actions/SetMaxNetworkLimit.s.sol) and configure these settings:

```solidity [SetMaxNetworkLimit.s.sol]
// Address of the Network
address NETWORK = 0x0000000000000000000000000000000000000000;
// Address of the Vault
address VAULT = 0x0000000000000000000000000000000000000000;
// Maximum amount of delegation that network is ready to receive
uint256 MAX_NETWORK_LIMIT = 0;
// Delay for the action to be executed
uint256 DELAY = 0;

// Optional

// Subnetwork Identifier (multiple subnetworks can be used, e.g., to have different max network limits for the same network)
uint96 SUBNETWORK_IDENTIFIER = 0;
// Salt for TimelockController operations
bytes32 SALT = "SetMaxNetworkLimit";
```

##### Edit the configuration fields, then choose an operation:

- `runS()` - schedule an action
- `runE()` - execute an action
- `runSE()` - schedule and execute an action (only if the delay is zero)

##### Run the operation:

- To execute the script with an EOA:

  ```bash [bash]
  forge script script/actions/SetMaxNetworkLimit.s.sol:SetMaxNetworkLimit --sig "runS()" --rpc-url <RPC_URL> --private-key <PRIVATE_KEY> --broadcast
  ```

- To get transaction calldata for a [Safe](https://app.safe.global/) multisig:

  ```bash [bash]
  forge script script/actions/SetMaxNetworkLimit.s.sol:SetMaxNetworkLimit --sig "runS()" --rpc-url <RPC_URL> --sender <MULTISIG_ADDRESS> --unlocked
  ```

  The logs will show a `callData` field like this:

  ```bash [Expected output]
  callData:0x01d5062a00000000000000000000000025ed2ee6e295880326bdeca245ee4d8b72c8f103000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000005365744d61784e6574776f726b4c696d697400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004423f752d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b6700000000000000000000000000000000000000000000000000000000
  ```

  In [Safe](https://app.safe.global/) Transaction Builder:

  1. Enable "Custom data"
  2. Enter the **Network's address** as the target address
  3. Use the `callData` (e.g., `0x01d5062a0000000000000000000000...`) received earlier as the `Data (Hex encoded)`
:::

## Update Delays

Every network action is protected by a delay (from zero to infinity).

Use these update delay scripts for the actions above, plus additional ones:

- [SetMaxNetworkLimitUpdateDelay](https://github.com/symbioticfi/network/blob/main/script/update-delay/SetMaxNetworkLimitUpdateDelay.s.sol)
- [SetResolverUpdateDelay](https://github.com/symbioticfi/network/blob/main/script/update-delay/SetResolverUpdateDelay.s.sol)
- [SetMiddlewareUpdateDelay](https://github.com/symbioticfi/network/blob/main/script/update-delay/SetMiddlewareUpdateDelay.s.sol)
- [UpgradeProxyUpdateDelay](https://github.com/symbioticfi/network/blob/main/script/update-delay/UpgradeProxyUpdateDelay.s.sol)
- [HotActionsUpdateDelay](https://github.com/symbioticfi/network/blob/main/script/update-delay/HotActionsUpdateDelay.s.sol) - update a delay for [SetMaxNetworkLimitUpdateDelay](https://github.com/symbioticfi/network/blob/main/script/update-delay/SetMaxNetworkLimitUpdateDelay.s.sol) and [SetResolverUpdateDelay](https://github.com/symbioticfi/network/blob/main/script/update-delay/SetResolverUpdateDelay.s.sol)
- [ColdActionsUpdateDelay](https://github.com/symbioticfi/network/blob/main/script/update-delay/ColdActionsUpdateDelay.s.sol) - update a delay for [SetMiddlewareUpdateDelay](https://github.com/symbioticfi/network/blob/main/script/update-delay/SetMiddlewareUpdateDelay.s.sol) and [UpgradeProxyUpdateDelay](https://github.com/symbioticfi/network/blob/main/script/update-delay/UpgradeProxyUpdateDelay.s.sol)
- [DefaultUpdateDelay](https://github.com/symbioticfi/network/blob/main/script/update-delay/DefaultUpdateDelay.s.sol) - update a delay for unconstrained actions
- [ArbitraryUpdateDelay](https://github.com/symbioticfi/network/blob/main/script/update-delay/ArbitraryCallUpdateDelay.s.sol) - update a delay for an arbitrary call:
  - set a delay for the exact target address and the exact selector
  - set a delay for any target address and the exact selector (by setting target address to `0x0000000000000000000000000000000000000000`)

For usage examples, see [Manage Your Network](/integrate/networks/network-contract#manage-your-network).

***

## Dashboard

:::warning
Work-in-progress, use with caution.
:::

The `Network.sol` contract inherits [OpenZeppelin](https://www.openzeppelin.com/)'s `TimelockController`, which inherits `AccessControl`. You can't determine the full state (operation statuses or role holders) using only RPC calls.

Use the Network Dashboard to:

- Get delays for all operations
- Get holders of any role
- Get scheduled/executed operations
- Schedule/execute arbitrary actions

:::info
Network Dashboard UI: [https://network-sage-rho.vercel.app/](https://network-sage-rho.vercel.app/)
:::

***

## Next Steps

<div
  style={{
      display: "grid",
      gridTemplateColumns: "repeat(auto-fit, minmax(300px, 1fr))",
      gap: "24px",
      marginBottom: "48px",
  }}
>
  <Card1
    title="Submit Metadata"
    description="Submit your network's metadata to make it visible on the Symbiotic UI."
    icon={
      <svg width="48" height="48" viewBox="0 0 48 48" fill="none">
          <rect
              x="8"
              y="8"
              width="32"
              height="32"
              rx="2"
              stroke="currentColor"
              strokeWidth="2"
              fill="none"
          />
          <rect x="12" y="12" width="24" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="18" width="20" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="24" width="16" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="30" width="18" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
      </svg>
  }
    href="/integrate/networks/submit-metadata"
  />
</div>


---

## /integrate/networks/pre-deposit

---
description: "You may want to curate or deploy vaults yourself in these cases:"
---

import { Card1 } from "../../../components/Card1";

# Deploy Pre-deposit Vault

You may want to curate or deploy vaults yourself in these cases:

- A DAO-controlled vault with your network's native asset as collateral
- Your network needs a specific vault configuration for onboarding

See the [vault deployment guide](/integrate/curators/deploy-vault) for details.

## Next Steps

<div
  style={{
      display: "grid",
      gridTemplateColumns: "repeat(auto-fit, minmax(300px, 1fr))",
      gap: "24px",
      marginBottom: "48px",
  }}
>
  <Card1
    title="Rewards"
    description="Learn how to integrate Symbiotic Rewards into your system."
    icon={
      <svg width="48" height="48" viewBox="0 0 48 48" fill="none">
          <rect
              x="8"
              y="8"
              width="32"
              height="32"
              rx="2"
              stroke="currentColor"
              strokeWidth="2"
              fill="none"
          />
          <rect x="12" y="12" width="24" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="18" width="20" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="24" width="16" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="30" width="18" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
      </svg>
  }
    href="/integrate/networks/rewards"
  />
</div>


---

## /integrate/networks/relay-offchain

---
description: ":::warning The code is a work in progress and not production ready yet. Breaking changes may occur in code updates, and backward compatibility is not..."
---

import { Card1 } from "../../../components/Card1";

# Relay Off-Chain

:::warning
The code is a work in progress and not production ready yet. Breaking changes may occur in code updates, and backward compatibility is not guaranteed. Use with caution.
:::

The Symbiotic Relay operates as a distributed middleware layer that facilitates:

- Validator Set Management: Derives and maintains validator sets across different epochs based on on-chain state
- Signature Aggregation: Collects individual validator signatures and aggregates them using BLS signatures or zero-knowledge proofs
- Cross-Chain Coordination: Manages validator sets across multiple EVM-compatible blockchains

## Architecture

The relay consists of several key components:

- **P2P Layer**: Uses libp2p with GossipSub for decentralized communication
- **Signer Nodes**: Sign messages using BLS/ECDSA keys
- **Aggregator Nodes**: Collect and aggregate signatures with configurable policies
- **Committer Nodes**: Submit aggregated proofs to settlement chains
- **API Server**: Exposes gRPC API for external clients

For detailed architecture information, see [here](https://github.com/symbioticfi/relay/blob/dev/DEVELOPMENT.md).

## Configure

:::info
For a complete reference of all configuration options and command-line flags, see the [relay\_sidecar CLI documentation](https://github.com/symbioticfi/relay/tree/dev/docs/cli/relay/relay_sidecar.md).
:::

The values you fill in come from the network you're joining:

| Field                                | What to put                                        | Where it comes from                                                                                                                     |
| ------------------------------------ | -------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `driver.address` + `driver.chain-id` | The network's `ValSetDriver` and its chain         | The network operator (see [Relay Utils → What you need](/integrate/networks/relay-utils#what-you-need)).                                |
| `evm.chains`                         | One RPC URL per chain the network uses             | A node/provider for each chain in the driver's `getVotingPowerProviders()` ∪ `getSettlements()` ∪ driver chain.                         |
| `secret-keys`                        | Your relay signing key(s) + an EVM key + a p2p key | Your keys — relay keys are the ones you registered in `KeyRegistry`; the relay key tag must match the network's `RequiredHeaderKeyTag`. |
| `p2p.bootnodes`                      | Multiaddr(s) of the network's bootnodes            | The network operator.                                                                                                                   |

Create a `config.yaml` file with the following structure:

```yaml [config.yaml]
# Logging
log:
    level: "debug" # Options: debug, info, warn, error
    mode: "pretty" # Options: json, text, pretty

# Storage
storage-dir: ".data" # Directory for persistent data
circuits-dir: "" # Path to ZK circuits (optional, empty disables ZK proofs)

# API Server
api:
    listen: ":8080" # API server address
    verbose-logging: false # Enable verbose API logging

# Metrics (optional)
metrics:
    listen: ":9090" # Metrics endpoint address
    pprof: false # Enable pprof debug endpoints

# Driver Contract
driver:
    chain-id: 31337 # Chain ID where driver contract is deployed
    address: "0x..." # Driver contract address

# Secret Keys
secret-keys:
    - namespace: "symb" # Namespace for the key
      key-type: 0 # 0=BLS-BN254, 1=ECDSA
      key-id: 15 # Key identifier
      secret: "0x..." # Private key hex

    - namespace: "evm"
      key-type: 1
      key-id: 31337
      secret: "0x..."

    - namespace: "p2p"
      key-type: 1
      key-id: 1
      secret: "0x..."

# Alternatively, use keystore
# keystore:
#   path: "/path/to/keystore.json"
#   password: "your-password"

# Signal Configuration, used for internal messages and event queues
signal:
    worker-count: 10 # Number of signal workers
    buffer-size: 20 # Signal buffer size

# Cache Configuration, used for in memorylookups for db queries
cache:
    network-config-size: 10 # Network config cache size
    validator-set-size: 10 # Validator set cache size

# Sync Configuration, sync signatures and proofs over p2p to recover missing information
sync:
    enabled: true # Enable P2P sync
    period: 5s # Sync period
    timeout: 1m # Sync timeout
    epochs: 5 # Number of epochs to sync

# Key Cache, used for fast public key lookups
key-cache:
    size: 100 # Key cache size
    enabled: true # Enable key caching

# P2P Configuration
p2p:
    listen: "/ip4/0.0.0.0/tcp/8880" # P2P listen address
    bootnodes: # List of bootstrap nodes (optional)
        - /dns4/node1/tcp/8880/p2p/...
    dht-mode: "server" # Options: auto, server, client, disabled, default: server (ideally should not change)
    mdns: true # Enable mDNS local discovery (useful for local networks)

# EVM Configuration
evm:
    chains: # List of settlement chain RPC endpoints
        - "http://localhost:8545"
        - "http://localhost:8546"
    max-calls: 30 # Max calls in multicall batches

# Aggregation Policy
aggregation-policy-max-unsigners: 50 # Max unsigners for low-cost policy
```

### Configure via Command-Line Flags

You can override config file values with command-line flags:

```bash
./relay_sidecar \
  --config config.yaml \
  --log.level debug \
  --storage-dir /var/lib/relay \
  --api.listen ":8080" \
  --p2p.listen "/ip4/0.0.0.0/tcp/8880" \
  --driver.chain-id 1 \
  --driver.address "0x..." \
  --secret-keys "symb/0/15/0x...,evm/1/31337/0x..." \
  --evm.chains "http://localhost:8545"
```

### Configure via Environment Variables

Environment variables use the `SYMB_` prefix with underscores instead of dashes and dots:

```bash
export SYMB_LOG_LEVEL=debug
export SYMB_LOG_MODE=pretty
export SYMB_STORAGE_DIR=/var/lib/relay
export SYMB_API_LISTEN=":8080"
export SYMB_P2P_LISTEN="/ip4/0.0.0.0/tcp/8880"
export SYMB_DRIVER_CHAIN_ID=1
export SYMB_DRIVER_ADDRESS="0x..."

./relay_sidecar --config config.yaml
```

### Configuration Priority

Configuration is loaded in the following order (highest priority first):

1. Command-line flags
2. Environment variables (with `SYMB_` prefix)
3. Configuration file (specified by `--config`)

### External Voting-Power Providers

A network can source voting power from an **off-chain** gRPC service instead of an EVM
`VotingPowerProvider` contract. Routing is decided by the provider's chain ID in the network config:
any `VotingPowerProvider` entry whose `chainId` falls in the reserved range
`4_000_000_000..4_100_000_000` is treated as external, and the sidecar queries the matching gRPC
endpoint you supply:

```bash
./relay_sidecar \
  --config config.yaml \
  --external-voting-power-provider id=<provider_id>,url=<grpc_url>
```

Optional settings can be appended: `secure=true`, `ca-cert-file=<path>`, `server-name=<sni>`,
`timeout=<duration>`, `headers=<k1:v1|k2:v2>`. `provider_id` is the first 10 bytes (20 hex chars)
of the on-chain `CrossChainAddress.Address`.

:::note
Operators of a network that uses an external provider skip on-chain stake setup (registering in the
EVM `VotingPowerProvider`, vault opt-in, deposit) for that provider — voting power comes from the
external service. On-chain providers in the same network still follow the normal flow.
:::

### Example

For reference, see how configurations are generated in the E2E setup:

```bash
# See the template in e2e/scripts/sidecar-start.sh (lines 11-27)
cat e2e/scripts/sidecar-start.sh
```

## Download and Run

Pre-built Docker images are available from Docker Hub:

:::steps
#### Pull the image

The latest image:

```bash [bash]
docker pull symbioticfi/relay:latest
```

Or a specific version:

```bash [bash]
docker pull symbioticfi/relay:<tag>
```

#### Run the relay sidecar

```bash [bash]
docker run -v $(pwd)/config.yaml:/config.yaml \
  symbioticfi/relay:latest \
  --config /config.yaml
```
:::

:::info
Docker Hub: [https://hub.docker.com/r/symbioticfi/relay](https://hub.docker.com/r/symbioticfi/relay)
:::

## API

The relay exposes both gRPC and HTTP/JSON REST APIs for interacting with the network:

### gRPC API

- [**API Documentation**](https://github.com/symbioticfi/relay/tree/dev/docs/api/v1/doc.md)
- [**Proto Definitions**](https://github.com/symbioticfi/relay/blob/dev/api/proto/v1/api.proto)
- [**Go Client**](https://github.com/symbioticfi/relay/tree/dev/api/client/v1/)
- [**Client Examples**](https://github.com/symbioticfi/relay/tree/dev/api/client/examples/)

### HTTP/JSON REST API Gateway

The relay includes an optional HTTP/JSON REST API gateway that translates HTTP requests to gRPC:

- [**Swagger File**](https://github.com/symbioticfi/relay/tree/dev/docs/api/v1/api.swagger.json)
- **Endpoints**: All gRPC methods accessible via RESTful HTTP at `/api/v1/*`

Enable via configuration:

```yaml [config.yaml]
api:
    http-gateway: true
```

Or via command-line flag:

```bash [bash]
./relay_sidecar --api.http-gateway=true
```

### Client Libraries

| Language   | Repository                                                           |
| ---------- | -------------------------------------------------------------------- |
| Go         | [relay](https://github.com/symbioticfi/relay/tree/dev/api/client/v1) |
| TypeScript | [relay-client-ts](https://github.com/symbioticfi/relay-client-ts)    |
| Rust       | [relay-client-rs](https://github.com/symbioticfi/relay-client-rs)    |

:::note
Use the HTTP gateway for language-agnostic access if needed.
:::

### Snippets

Check out multiple simple snippets how to use the clients mentioned above:

:::code-group
<div data-title="Go">
  ```go [myApp.go]
  import (
      relay "github.com/symbioticfi/relay/api/client/v1"
  )

  func main() {
      relayClient = relay.NewSymbioticClient(conn)

      epochInfos, _ := relayClient.GetLastAllCommitted(ctx, &relay.GetLastAllCommittedRequest{})
      suggestedEpoch := epochInfos.SuggestedEpochInfo.GetLastCommittedEpoch()

      signMessageResponse, _ := relayClient.SignMessage(ctx, &relay.SignMessageRequest{
          KeyTag:        15, // default key tag - BN254
          Message:       encode(taskId),
          RequiredEpoch: &suggestedEpoch,
      })

      listenProofsRequest := &relay.ListenProofsRequest{
          StartEpoch: suggestedEpoch,
      }
      proofsStream, _ := relayClient.ListenProofs(ctx, listenProofsRequest)
      aggregationProof = []byte{}
      while proofResponse, _ := proofsStream.Recv() {
          if proofResponse.GetRequestId() == signMessageResponse.GetRequestId() {
              aggregationProof = proofResponse.GetAggregationProof().GetProof()
              break
          }
      }

      appContract.CompleteTask(taskId, signMessageResponse.Epoch, aggregationProof)
  }
  ```
</div>

<div data-title="TypeScript">
  ```go [myApp.ts]
  import { createClient } from "@connectrpc/connect";
  import { SymbioticAPIService } from "@symbioticfi/relay-client-ts";
  import {
    GetLastAllCommittedRequestSchema,
    SignMessageRequestSchema,
    ListenProofsRequestSchema,
  } from "@symbioticfi/relay-client-ts";
  import { create } from "@bufbuild/protobuf";

  async function main() {
      const relayClient = createClient(SymbioticAPIService, transport);

      const getLastAllCommittedResponse = await relayClient.getLastAllCommitted(create(GetLastAllCommittedRequestSchema));
      const suggestedEpoch = getLastAllCommittedResponse.suggestedEpochInfo.lastCommittedEpoch;

      const signMessageRequest = create(SignMessageRequestSchema, {
          keyTag: 15, // default key tag - BN254
          message: encode(taskId),
          requiredEpoch: suggestedEpoch,
      });
      const signMessageResponse = await relayClient.signMessage(signMessageRequest);

      const listenProofsRequest = create(ListenProofsRequestSchema, { startEpoch: suggestedEpoch });
      const proofsStream = await relayClient.listenProofs(listenProofsRequest);
      let aggregationProof;
      for await (const proofResponse of proofsStream) {
          if (proofResponse.requestId === signMessageResponse.requestId) {
              aggregationProof = proofResponse.aggregationProof?.proof;
              break;
          }
      }

      await appContract.completeTask(taskId, signMessageResponse.epoch, aggregationProof);
  }
  ```
</div>

<div data-title="Rust">
  ```go [my_app.rs]
  use symbiotic_relay_client::generated::api::proto::v1::{
      GetLastAllCommittedRequest, SignMessageRequest, ListenProofsRequest,
      symbiotic_api_service_client::SymbioticApiServiceClient,
  };

  #[tokio::main]
  async fn main() -> Result<(), Box<dyn std::error::Error>> {
      let mut relay_client = SymbioticApiServiceClient::new(channel);

      let epoch_infos_response = relay_client.get_last_all_committed(tonic::Request::new(GetLastAllCommittedRequest {})).await?;
      let epoch_infos_data = epoch_infos_response.into_inner();
      let mut suggested_epoch = epoch_infos_data.suggested_epoch_info.last_committed_epoch;

     let sign_request = tonic::Request::new(SignMessageRequest {
          key_tag: 15, // default key tag - BN254
          message: encode(task_id).into(),
          required_epoch: suggested_epoch,
      });
      let sign_response = relay_client.sign_message(sign_request).await?;
      let sign_data = sign_response.into_inner();

      let listen_proofs_request = tonic::Request::new(ListenProofsRequest { start_epoch: suggested_epoch });
      let proofs_stream = relay_client.listen_proofs(listen_proofs_request).await?.into_inner();
      let mut aggregation_proof = None;
      while let Some(proof_response) = proofs_stream.next().await {
          match proof_response {
              Ok(proof_data) => {
                  if proof_data.request_id == sign_data.request_id {
                      if let Some(proof) = proof_data.aggregation_proof {
                          aggregation_proof = Some(proof.proof);
                          break;
                      }
                  }
              }
              Err(e) => { break; }
          }
      }

      appContract.complete_task(task_id, sign_data.epoch, aggregation_proof.unwrap()).await?;
  }
  ```
</div>
:::

## Integration Examples

For a complete end-to-end examples application using the relay, see:

| Repository                                                                | Description                                                   |
| ------------------------------------------------------------------------- | ------------------------------------------------------------- |
| [Symbiotic Super Sum](https://github.com/symbioticfi/symbiotic-super-sum) | A simple task-based network                                   |
| [Cosmos Relay SDK](https://github.com/symbioticfi/cosmos-relay-sdk)       | An application built using the Cosmos SDK and Symbiotic Relay |

## Next Steps

<div
  style={{
      display: "grid",
      gridTemplateColumns: "repeat(auto-fit, minmax(300px, 1fr))",
      gap: "24px",
      marginBottom: "48px",
  }}
>
  <Card1
    title="Helpful Core Contracts' Endpoints"
    description="Check out core contracts' endpoints that may help you during integration."
    icon={
          <svg width="48" height="48" viewBox="0 0 48 48" fill="none">
              <rect
                  x="8"
                  y="8"
                  width="32"
                  height="32"
                  rx="2"
                  stroke="currentColor"
                  strokeWidth="2"
                  fill="none"
              />
              <rect
                  x="12"
                  y="12"
                  width="24"
                  height="3"
                  rx="1.5"
                  fill="currentColor"
                  opacity="0.3"
              />
              <rect
                  x="12"
                  y="18"
                  width="20"
                  height="3"
                  rx="1.5"
                  fill="currentColor"
                  opacity="0.3"
              />
              <rect
                  x="12"
                  y="24"
                  width="16"
                  height="3"
                  rx="1.5"
                  fill="currentColor"
                  opacity="0.3"
              />
              <rect
                  x="12"
                  y="30"
                  width="18"
                  height="3"
                  rx="1.5"
                  fill="currentColor"
                  opacity="0.3"
              />
          </svg>
      }
    href="/integrate/networks/helpful-core-contracts-endpoints"
  />
</div>


---

## /integrate/networks/relay-onchain

---
description: "The Symbiotic Relay system implements a complete signature aggregation workflow from validator set derivation through on-chain commitment of the ValSetHeader..."
---

import { Card1 } from "../../../components/Card1";

# Relay On-Chain

The Symbiotic Relay system implements a complete signature aggregation workflow from validator set derivation through on-chain commitment of the ValSetHeader data structure. This enables provable attestation checks on any chain for arbitrary data signed by the validator set quorum.

The system provides a modular smart contract framework that lets you manage validator sets dynamically, handle cryptographic keys, aggregate signatures, and commit cross-chain state.

## Architecture

Symbiotic provides a set of predefined smart contracts representing the following modules:

- [`VotingPowerProvider`](https://github.com/symbioticfi/relay-contracts/blob/main/src/modules/voting-power/) - provides basic data about operators, vaults, and their voting power, enabling various onboarding schemes
- [`KeyRegistry`](https://github.com/symbioticfi/relay-contracts/blob/main/src/modules/key-registry/) - verifies and manages operators' keys; currently, these key types are supported:
  - [`BlsBn254`](https://github.com/symbioticfi/relay-contracts/blob/main/src/libraries/keys/KeyBlsBn254.sol) ([signature verification](https://github.com/symbioticfi/relay-contracts/blob/main/src/libraries/sigs/SigBlsBn254.sol))
  - [`EcdsaSecp256k1`](https://github.com/symbioticfi/relay-contracts/blob/main/src/libraries/keys/KeyEcdsaSecp256k1.sol) ([signature verification](https://github.com/symbioticfi/relay-contracts/blob/main/src/libraries/sigs/SigEcdsaSecp256k1.sol))
- [`ValSetDriver`](https://github.com/symbioticfi/relay-contracts/blob/main/src/modules/valset-driver/) - used by the off-chain part of Symbiotic Relay for validator set derivation and maintenance
- [`Settlement`](https://github.com/symbioticfi/relay-contracts/blob/main/src/modules/settlement/) - requires committing a compressed validator set (header) each epoch, but allows verifying signatures made by the validator set; currently supports the following verification mechanics:
  - [`SimpleVerifier`](https://github.com/symbioticfi/relay-contracts/blob/main/src/modules/settlement/sig-verifiers/SigVerifierBlsBn254Simple.sol) - requires the whole validator set as an input during verification, but in a compressed and efficient way, making it the best choice for up to 125 validators
  - [`ZKVerifier`](https://github.com/symbioticfi/relay-contracts/blob/main/src/modules/settlement/sig-verifiers/SigVerifierBlsBn254ZK.sol) - uses ZK verification made with [gnark](https://github.com/Consensys/gnark), allowing larger validator sets with an almost constant verification gas cost

:::info
For Symbiotic Core's `NetworkMiddlewareService` contract, set the `VotingPowerProvider` contract as the middleware.
:::

### Permissions

Relay contracts have three ready-to-use permission models:

- [`OzOwnable`](https://github.com/symbioticfi/relay-contracts/blob/main/src/modules/common/permissions/OzOwnable.sol) - allows setting an owner address that can perform permissioned actions
  - Based on [OpenZeppelin's `Ownable` contract](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol)
- [`OzAccessControl`](https://github.com/symbioticfi/relay-contracts/blob/main/src/modules/common/permissions/OzAccessControl.sol) - enables role-based permissions for each action
  - Based on [OpenZeppelin's `AccessControl` contract](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/AccessControl.sol)
  - Roles can be assigned to function selectors using `_setSelectorRole(bytes4 selector, bytes32 role)`
- [`OzAccessManaged`](https://github.com/symbioticfi/relay-contracts/blob/main/src/modules/common/permissions/OzAccessManaged.sol) - controls which callers can access specific functions
  - Based on [OpenZeppelin's `AccessManaged` contract](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/manager/AccessManager.sol)

To use these permission models, inherit one of the above contracts and add the `checkPermission` modifier to functions that require access control.

:::info
Only one permission model can be used at a time.
:::

#### Examples

##### ValSetDriver with OzOwnable

```solidity [MyValSetDriver.sol]
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import {ValSetDriver} from "../src/modules/valset-driver/ValSetDriver.sol";
import {OzOwnable} from "../src/modules/common/permissions/OzOwnable.sol";
import {IEpochManager} from "../src/interfaces/modules/valset-driver/IEpochManager.sol";
import {IValSetDriver} from "../src/interfaces/modules/valset-driver/IValSetDriver.sol";

contract MyValSetDriver is ValSetDriver, OzOwnable {
    function initialize(
        ValSetDriverInitParams memory valSetDriverInitParams,
        address owner
    ) public virtual initializer {
        __ValSetDriver_init(valSetDriverInitParams);
        __OzOwnable_init(ozOwnableInitParams);
    }
}
```

##### Settlement with OzAccessControl

```solidity [MySettlement.sol]
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import {Settlement} from "../src/modules/settlement/Settlement.sol";
import {OzAccessControl} from "../src/modules/common/permissions/OzAccessControl.sol";
import {ISettlement} from "../src/interfaces/modules/settlement/ISettlement.sol";

contract MySettlement is Settlement, OzAccessControl {
    bytes32 public constant SET_SIG_VERIFIER_ROLE = keccak256("SET_SIG_VERIFIER_ROLE");
    bytes32 public constant SET_GENESIS_ROLE = keccak256("SET_GENESIS_ROLE");

    function initialize(
        SettlementInitParams memory settlementInitParams,
        address defaultAdmin
    ) public virtual initializer {
        __Settlement_init(settlementInitParams);

        _grantRole(DEFAULT_ADMIN_ROLE, defaultAdmin);

        _setSelectorRole(ISettlement.setSigVerifier.selector, SET_SIG_VERIFIER_ROLE);
        _setSelectorRole(ISettlement.setGenesis.selector, SET_GENESIS_ROLE);
    }
}
```

### VotingPowerProvider Extensions

Multiple voting power extensions can be combined to achieve different properties of the VotingPowerProvider:

- [`OperatorsWhitelist`](https://github.com/symbioticfi/relay-contracts/blob/main/src/modules/voting-power/extensions/OperatorsWhitelist.sol) - only whitelisted operators can register
- [`OperatorsBlacklist`](https://github.com/symbioticfi/relay-contracts/blob/main/src/modules/voting-power/extensions/OperatorsBlacklist.sol) - blacklisted operators are unregistered and are forbidden to return back
- [`OperatorsJail`](https://github.com/symbioticfi/relay-contracts/blob/main/src/modules/voting-power/extensions/OperatorsJail.sol) - operators can be jailed for some amount of time and register back after that
- [`SharedVaults`](https://github.com/symbioticfi/relay-contracts/blob/main/src/modules/voting-power/extensions/SharedVaults.sol) - shared (with other networks) vaults (like the ones with NetworkRestakeDelegator) can be added
- [`OperatorVaults`](https://github.com/symbioticfi/relay-contracts/blob/main/src/modules/voting-power/extensions/OperatorVaults.sol) - vaults that are attached to a single operator can be added
- [`MultiToken`](https://github.com/symbioticfi/relay-contracts/blob/main/src/modules/voting-power/extensions/MultiToken.sol) - possible to add new supported tokens on the go
- [`OpNetVaultAutoDeploy`](https://github.com/symbioticfi/relay-contracts/blob/main/src/modules/voting-power/extensions/OpNetVaultAutoDeploy.sol) - enable auto-creation of the configured by you vault on each operator registration
- Ready bindings are also available for [slashing](https://github.com/symbioticfi/relay-contracts/blob/main/src/modules/voting-power/extensions/BaseSlashing.sol) and [rewards](https://github.com/symbioticfi/relay-contracts/blob/main/src/modules/voting-power/extensions/BaseRewards.sol)

#### Examples

##### Single-Operator Vaults Added by Owner

```solidity [MyVotingPowerProvider.sol]
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import {VotingPowerProvider} from "../src/modules/voting-power/VotingPowerProvider.sol";
import {OzOwnable} from "../src/modules/common/permissions/OzOwnable.sol";
import {EqualStakeVPCalc} from "../src/modules/voting-power/common/voting-power-calc/EqualStakeVPCalc.sol";
import {OperatorVaults} from "../src/modules/voting-power/extensions/OperatorVaults.sol";

contract MyVotingPowerProvider is VotingPowerProvider, OzOwnable, EqualStakeVPCalc, OperatorVaults {
    constructor(address operatorRegistry, address vaultFactory) VotingPowerProvider(operatorRegistry, vaultFactory) {}

    function initialize(
        VotingPowerProviderInitParams memory votingPowerProviderInitParams,
        OzOwnableInitParams memory ozOwnableInitParams
    ) public virtual initializer {
        __VotingPowerProvider_init(votingPowerProviderInitParams);
        __OzOwnable_init(ozOwnableInitParams);
    }
}
```

##### Shared Vaults Whitelisted under AccessControl

```solidity [MyVotingPowerProvider.sol]
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import {VotingPowerProvider} from "../src/modules/voting-power/VotingPowerProvider.sol";
import {OzAccessControl} from "../src/modules/common/permissions/OzAccessControl.sol";
import {EqualStakeVPCalc} from "../src/modules/voting-power/common/voting-power-calc/EqualStakeVPCalc.sol";
import {SharedVaults} from "../src/modules/voting-power/extensions/SharedVaults.sol";
import {OperatorsWhitelist} from "../src/modules/voting-power/extensions/OperatorsWhitelist.sol";
import {ISharedVaults} from "../src/interfaces/modules/voting-power/extensions/ISharedVaults.sol";

contract MyVotingPowerProvider is VotingPowerProvider, OzAccessControl, EqualStakeVPCalc, SharedVaults, OperatorsWhitelist {
    bytes32 public constant REGISTER_SHARED_VAULT = keccak256("REGISTER_SHARED_VAULT");
    bytes32 public constant UNREGISTER_SHARED_VAULT = keccak256("UNREGISTER_SHARED_VAULT");
    bytes32 public constant SET_WHITELIST_STATUS = keccak256("SET_WHITELIST_STATUS");
    bytes32 public constant WHITELIST_OPERATOR = keccak256("WHITELIST_OPERATOR");
    bytes32 public constant UNWHITELIST_OPERATOR = keccak256("UNWHITELIST_OPERATOR");

    constructor(address operatorRegistry, address vaultFactory) VotingPowerProvider(operatorRegistry, vaultFactory) {}

    function initialize(
        VotingPowerProviderInitParams memory votingPowerProviderInitParams,
        address defaultAdmin
    ) public virtual initializer {
        __VotingPowerProvider_init(votingPowerProviderInitParams);

        _grantRole(DEFAULT_ADMIN_ROLE, defaultAdmin);
        _setSelectorRole(ISharedVaults.registerSharedVault.selector, REGISTER_SHARED_VAULT);
        _setSelectorRole(ISharedVaults.unregisterSharedVault.selector, UNREGISTER_SHARED_VAULT);
        _setSelectorRole(ISharedVaults.setWhitelistStatus.selector, SET_WHITELIST_STATUS);
        _setSelectorRole(ISharedVaults.whitelistOperator.selector, WHITELIST_OPERATOR);
        _setSelectorRole(ISharedVaults.unwhitelistOperator.selector, UNWHITELIST_OPERATOR);
    }

    function _registerOperatorImpl(
        address operator
    ) internal virtual override(VotingPowerProvider, OperatorsWhitelist) {
        super._registerOperatorImpl(operator);
    }
}
```

### VotingPowerProvider Power Calculators

`VotingPowerProvider` always inherits a virtual [VotingPowerCalculators](https://github.com/symbioticfi/relay-contracts/blob/main/src/modules/voting-power/common/voting-power-calc/) contracts that has to be implemented in the resulting contract.
Symbiotic provides several stake-to-votingPower conversion mechanisms you can separately or combine:

- [EqualStakeVPCalc](https://github.com/symbioticfi/relay-contracts/blob/main/src/modules/voting-power/common/voting-power-calc/EqualStakeVPCalc.sol) - voting power is equal to stake
- [NormalizedTokenDecimalsVPCalc](https://github.com/symbioticfi/relay-contracts/blob/main/src/modules/voting-power/common/voting-power-calc/NormalizedTokenDecimalsVPCalc.sol) - all tokens' decimals are normalized to 18
- [PricedTokensChainlinkVPCalc](https://github.com/symbioticfi/relay-contracts/blob/main/src/modules/voting-power/common/voting-power-calc/PricedTokensChainlinkVPCalc.sol) - voting power is calculated using Chainlink price feeds
- [WeightedTokensVPCalc](https://github.com/symbioticfi/relay-contracts/blob/main/src/modules/voting-power/common/voting-power-calc/WeightedTokensVPCalc.sol) - voting power is affected by configured weights for tokens
- [WeightedVaultsVPCalc](https://github.com/symbioticfi/relay-contracts/blob/main/src/modules/voting-power/common/voting-power-calc/WeightedVaultsVPCalc.sol) - voting power is affected by configured weights for vaults

#### Examples

##### Chainlink-Priced Stake with Token-/Vault-Specific Weights

```solidity [MyVotingPowerProvider.sol]
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import {VotingPowerProvider} from "../src/modules/voting-power/VotingPowerProvider.sol";
import {PricedTokensChainlinkVPCalc} from
    "../src/modules/voting-power/common/voting-power-calc/PricedTokensChainlinkVPCalc.sol";
    import {OzOwnable} from "../src/modules/common/permissions/OzOwnable.sol";
import {WeightedTokensVPCalc} from "../src/modules/voting-power/common/voting-power-calc/WeightedTokensVPCalc.sol";
import {WeightedVaultsVPCalc} from "../src/modules/voting-power/common/voting-power-calc/WeightedVaultsVPCalc.sol";
import {VotingPowerCalcManager} from "../src/modules/voting-power/base/VotingPowerCalcManager.sol";

contract MyVotingPowerProvider is VotingPowerProvider, OzOwnable, PricedTokensChainlinkVPCalc, WeightedTokensVPCalc, WeightedVaultsVPCalc {
    constructor(address operatorRegistry, address vaultFactory) VotingPowerProvider(operatorRegistry, vaultFactory) {}

    function initialize(
        VotingPowerProviderInitParams memory votingPowerProviderInitParams,
        OzOwnableInitParams memory ozOwnableInitParams
    ) public virtual initializer {
        __VotingPowerProvider_init(votingPowerProviderInitParams);
        __OzOwnable_init(ozOwnableInitParams);
    }

    function stakeToVotingPowerAt(
        address vault,
        uint256 stake,
        bytes memory extraData,
        uint48 timestamp
    )
        public
        view
        override(VotingPowerCalcManager, PricedTokensChainlinkVPCalc, WeightedTokensVPCalc, WeightedVaultsVPCalc)
        returns (uint256)
    {
        return super.stakeToVotingPowerAt(vault, stake, extraData, timestamp);
    }

    function stakeToVotingPower(
        address vault,
        uint256 stake,
        bytes memory extraData
    )
        public
        view
        override(VotingPowerCalcManager, PricedTokensChainlinkVPCalc, WeightedTokensVPCalc, WeightedVaultsVPCalc)
        returns (uint256)
    {
        return super.stakeToVotingPower(vault, stake, extraData);
    }
}
```

:::note
If too many extensions/additions are implemented, the contract may exceed the maximum bytecode size. In this case, try adding `via-ir` flag with low number of optimizer runs for Solidity compiler.
:::

## Deployment

The deployment tooling is in the [`script/`](https://github.com/symbioticfi/relay-contracts/tree/main/script) folder. It consists of [`RelayDeploy.sol`](https://github.com/symbioticfi/relay-contracts/tree/main/script/RelayDeploy.sol) Foundry script template and [`relay-deploy.sh`](https://github.com/symbioticfi/relay-contracts/tree/main/script/relay-deploy.sh) bash script (Relay smart contracts use external libraries, so it's not currently possible to use solely Foundry script for multi-chain deployment).

- [`RelayDeploy.sol`](https://github.com/symbioticfi/relay-contracts/tree/main/script/RelayDeploy.sol) - abstract base that wires common Symbiotic Core helpers and exposes four deployment hooks: KeyRegistry, VotingPowerProvider, Settlement, and ValSetDriver
- [`relay-deploy.sh`](https://github.com/symbioticfi/relay-contracts/tree/main/script/relay-deploy.sh) - orchestrates per-contract multi-chain deployments (uses Python to parse `toml` file)

The script deploys Relay modules under [OpenZeppelin's TransparentUpgradeableProxy](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/transparent/TransparentUpgradeableProxy.sol) using [CreateX](https://github.com/pcaversaccio/createx), providing better control for production deployments and simpler approaches for development.

:::info
**Prerequisites.** Clone with submodules and build first — the script compiles the whole project, including the nested Symbiotic Core / forge-std submodules:

```bash [bash]
git clone --recurse-submodules https://github.com/symbioticfi/relay-contracts.git
cd relay-contracts
git submodule update --init --recursive   # ensure nested submodules are present
forge build
```

A shallow clone without submodules fails with `Source "…/forge-std/src/Test.sol" not found`.
:::

::::steps
### Configure on-chain deployment

Implement your `MyRelayDeploy.sol` ([see example](https://github.com/symbioticfi/relay-contracts/tree/main/script/examples/MyRelayDeploy.sol)):

- Include the deployment configuration of your Relay modules
- Implement all virtual functions of `RelayDeploy.sol`
- In the constructor, input the path of the `toml` file
- Use additional helpers such as `getCore()`, `getKeyRegistry()`, `getVotingPowerProvider()`, etc. (see full list in [`RelayDeploy.sol`](https://github.com/symbioticfi/relay-contracts/tree/main/script/RelayDeploy.sol))
- See [Deployment Parameters](#deployment-parameters) below for what each `MyRelayDeploy.sol` constant controls

### Choose multi-chain setup

Implement your `my-relay-deploy.toml` ([see example](https://github.com/symbioticfi/relay-contracts/tree/main/script/examples/my-relay-deploy.toml)). Two kinds of entries: one block per chain with its RPC URL, and one sentinel block mapping each module to the chain(s) it deploys to.

```toml [my-relay-deploy.toml]
# One block per chain you deploy to — the table name is the chain ID.
[11155111]                                    # e.g. Sepolia
endpoint_url = "https://sepolia.example/rpc"   # RPC URL for that chain

[11155420]                                    # e.g. OP Sepolia
endpoint_url = "https://op-sepolia.example/rpc"

# Deployment map. Keep the literal [1234567890] table name and the empty endpoint_url.
[1234567890]
endpoint_url = ""
keyRegistry         = 11155111                 # chain to deploy KeyRegistry on
votingPowerProvider = [11155111]               # one or more chains
settlement          = [11155111, 11155420]     # Settlement is commonly multi-chain
valSetDriver        = 11155111
```

- Use a chain ID + `endpoint_url` block for every chain referenced in the map.
- **Keep the `[1234567890]` block and its empty `endpoint_url` — it is a required placeholder, not a real chain.**
- Modules deploy in this order regardless: 1. KeyRegistry 2. VotingPowerProvider 3. Settlement 4. ValSetDriver. After the run, deployed addresses are written back into this file under `[<chainId>.address]`.

### Run the deployment

Execute the deployment script, e.g.:

```bash [bash]
# sign with a hardware wallet (--ledger) or an EOA (--private-key <DEPLOYER_KEY>)
./script/relay-deploy.sh ./script/examples/MyRelayDeploy.sol ./script/examples/my-relay-deploy.toml --broadcast --ledger
```

:::note
Basic form is `./script/relay-deploy.sh <FoundryScript> <TomlConfig> <Any Foundry Flags>` — anything after the two paths is forwarded to every `forge script` call (e.g. `--private-key`, `--ledger`, `-vvvv`). The signer pays gas on every chain in the map.
:::

At the end, your `toml` file will contain the addresses of the deployed Relay modules.

:::note
**Local chains:** Symbiotic Core isn't deployed on a fresh devnet, so the script bootstraps it automatically on first run (on Ethereum/Holešky/Sepolia/Hoodi it's already deployed and this is skipped). If you hit `FailedDeployment()` / `CreateCollision`, your chain or the `broadcast/` + `cache/` folders carry stale state from a previous attempt — restart the chain from a clean genesis and remove `broadcast/MyRelayDeploy.sol` and `cache/MyRelayDeploy.sol`, then re-run.
:::
::::

### Deployment Parameters

The example `MyRelayDeploy.sol` is a **template, not a ready-to-run network** — it deploys with `OWNER = NETWORK_ADDRESS = address(1)` and a minimal `VotingPowerProvider`. Before a real deployment you must, at minimum, set `OWNER` and `NETWORK_ADDRESS` to real values (a [registered network](/integrate/networks/network-contract)) and pick a `VotingPowerProvider` composition that lets operators actually carry stake — e.g. add `OpNetVaultAutoDeploy` so each operator gets a vault on registration, or wire shared/operator vaults explicitly (see [Relay On-Chain extensions](#votingpowerprovider-extensions) and [Manage Allocations](/integrate/curators/manage-allocations)).

The `initialize(...)` arguments are declared as constants at the top of the example
`MyRelayDeploy.sol`. Review these before any non-demo deployment:

| Constant                                                                   | Controls                                                          | Note                                                                                                                                                     |
| -------------------------------------------------------------------------- | ----------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `OWNER`                                                                    | Admin / `DEFAULT_ADMIN_ROLE` on the deployed modules              | Defaults to `address(1)` in the example — **set it to your multisig/governance**.                                                                        |
| `NETWORK_ADDRESS`                                                          | The network entity (from `NetworkRegistry`) bound to every module | Defaults to `address(1)` — **must be your registered network**. For `OpNetVaultAutoDeploy` it must be a contract implementing `ISetMaxNetworkLimitHook`. |
| `SUBNETWORK_ID`                                                            | Subnetwork identifier under your network                          | Usually `0`.                                                                                                                                             |
| `QUORUM_THRESHOLD`                                                         | Signing threshold per key tag                                     | **Scaled by `1e18` (so `1e18` = 100%).** The example's `6667` is a placeholder — a realistic value is e.g. `666666666666666667` (≈ 66.67%).              |
| `REQUIRED_HEADER_KEY_TAG`                                                  | Key tag used to sign validator-set headers                        | Must be a **BLS BN254** tag (the only aggregatable type), e.g. `15`.                                                                                     |
| `VERIFICATION_TYPE`                                                        | Settlement verifier selector                                      | Must match the verifier you wire into `Settlement`. Confirm after deploy with `getVerificationType()`.                                                   |
| `EPOCH_DURATION` / `COMMITTER_SLOT_DURATION`                               | Epoch length and committer slot, in seconds                       | —                                                                                                                                                        |
| `NUM_AGGREGATORS` / `NUM_COMMITTERS`                                       | Off-chain roles scheduled per epoch                               | —                                                                                                                                                        |
| `MAX_VOTING_POWER` / `MIN_INCLUSION_VOTING_POWER` / `MAX_VALIDATORS_COUNT` | Validator-set admission filters                                   | `0` disables the cap where applicable.                                                                                                                   |
| `*_SALT`                                                                   | CreateX salt per module                                           | Determines the deterministic proxy address; reuse the same salt to get identical addresses across chains.                                                |

**Where the addresses come from:**

- **Symbiotic Core** addresses the modules depend on (e.g. `operatorRegistry`, `vaultFactory` for the `VotingPowerProvider` constructor) are resolved automatically by the `getCore()` helper on supported chains — Ethereum, Holešky, Sepolia, and Hoodi. The canonical list is on the [Addresses](/get-started/resources/addresses#core) page.
- `NETWORK_ADDRESS` is your own network entity — register it in `NetworkRegistry` first (see [Network Contract](/integrate/networks/network-contract)).
- **Your deployed relay modules** (`KeyRegistry`, `VotingPowerProvider`, `Settlement`, `ValSetDriver`) are written back into your `my-relay-deploy.toml` once the script finishes. Hand the `ValSetDriver` address + its chain ID to operators — that is all they need for [Relay Utils](/integrate/networks/relay-utils) and the [sidecar](/integrate/networks/relay-offchain).

### After deploying: wire and go live

With the modules deployed, finish bringing the network online — **in this order**:

1. **Set your middleware** — from the network, call `NetworkMiddlewareService.setMiddleware(votingPowerProvider)` (the `SetMiddleware` action in the [network repo](/integrate/networks/network-contract#manage-your-network)). Required: until it's set, Core doesn't associate stake with your network and the relay derives an empty validator set.
2. **Wire vault limits** — the network calls `setMaxNetworkLimit`, the curator sets `setNetworkLimit` / operator shares (automatic for `OpNetVaultAutoDeploy`). See [Manage Allocations](/integrate/curators/manage-allocations).
3. **Onboard your initial operators** — they register in Core, opt into the network, register in your `VotingPowerProvider` with their keys, opt into a vault, and stake. Point them at [Add an operator](/integrate/operators).
4. **Commit genesis** — `utils network generate-genesis --commit` derives the first validator set from current on-chain voting power and commits the header to every Settlement chain. See [Relay Utils → Commit the genesis validator set](/integrate/networks/relay-utils#commit-the-genesis-validator-set).
5. **Go live** — operators run [`relay_sidecar`](/integrate/networks/relay-offchain); each epoch the set is derived, signatures aggregated, and committers post the header on-chain.

:::warning
Genesis reads the voting power that exists **at that moment** — make sure at least one operator is registered, keyed, and staked **before** you commit, or the genesis set is empty.
:::

## Integrate

Symbiotic Relay provides comprehensive tooling that works independently, so you can focus on your stake-backed application logic.

### Verify Message

Your application contract can verify any message using a validator set at any point in time via:

```solidity [MyApp.sol]
import {ISettlement} from "@symbioticfi/relay-contracts/src/interfaces/modules/settlement/ISettlement.sol";

function verifyMessage(bytes calldata message, uint48 epoch, bytes calldata proof) public returns (bool) {
    return ISettlement(SETTLEMENT).verifyQuorumSigAt(
            abi.encode(keccak256(message)),
            15, // default key tag - BN254
            (uint248(1e18) * 2) / 3 + 1, // default quorum threshold - 2/3 + 1
            proof,
            epoch,
            new bytes(0)
    );
}
```

:::note
You need to have a `Settlement` deployed on the verification chain first.
:::

### Use Validator Set Data

Your application contract can use the validator set at any point in time using [SSZ](https://ethereum.org/developers/docs/data-structures-and-encoding/ssz/) proof verification, for example:

```solidity [MyApp.sol]
import {ValSetVerifier} from "@symbioticfi/relay-contracts/src/libraries/utils/ValSetVerifier.sol";
import {Math} from "openzeppelin-contracts/contracts/utils/math/Math.sol";

function verifyOperatorVotingPower(
    ValSetVerifier.SszProof calldata validatorRootProof,
    uint256 validatorRootLocalIndex,
    bytes32 validatorSetRoot,
    ValSetVerifier.SszProof calldata operatorProof,
    address operator,
    ValSetVerifier.SszProof calldata votingPowerProof,
    uint256 votingPower
) public returns (bool) {
    return operatorProof.leaf == bytes32(uint256(uint160(operator)) << 96)
        && ValSetVerifier.verifyOperator(
        validatorRootProof, validatorRootLocalIndex, validatorSetRoot, operatorProof
    ) && votingPowerProof.leaf == bytes32(votingPower << (256 - (Math.log2(votingPower) / 8 + 1) * 8))
        && ValSetVerifier.verifyVotingPower(
        validatorRootProof, validatorRootLocalIndex, validatorSetRoot, votingPowerProof
    );
}
```

:::note
You need to have a `Settlement` deployed on the verification chain first.
:::

## Next Steps

<div
  style={{
      display: "grid",
      gridTemplateColumns: "repeat(auto-fit, minmax(300px, 1fr))",
      gap: "24px",
      marginBottom: "48px",
  }}
>
  <Card1
    title="Relay Off-Chain"
    description="Proceed to the the development of your protocol's off-chain part using Relay."
    icon={
      <svg width="48" height="48" viewBox="0 0 48 48" fill="none">
          <rect
              x="8"
              y="8"
              width="32"
              height="32"
              rx="2"
              stroke="currentColor"
              strokeWidth="2"
              fill="none"
          />
          <rect x="12" y="12" width="24" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="18" width="20" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="24" width="16" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="30" width="18" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
      </svg>
  }
    href="/integrate/networks/relay-offchain"
  />
</div>


---

## /integrate/networks/relay-utils

---
description: "relay_utils is the relay's key, operator, and network CLI — generate and register validator keys, commit the genesis validator set, and inspect operator status."
---

import { Card1 } from "../../../components/Card1";

# Relay Utils

`relay_utils` is the command-line tool that drives the **operational** steps of a Symbiotic Relay
network — the work that sits between deploying the [Relay contracts](/integrate/networks/relay-onchain)
and running the [Relay sidecar](/integrate/networks/relay-offchain):

- **Network operators** use it to commit the genesis validator set that bootstraps the network.
- **Validators/operators** use it to generate their signing keys, register them in `KeyRegistry`, and
  check whether they made it into the validator set.

:::info
`relay_utils` ships in the same image as the sidecar. Run it from the published Docker image:

```bash [bash]
docker run --rm -v $(pwd):/data --entrypoint relay_utils symbioticfi/relay:latest --help
```

or build it from [`symbioticfi/relay`](https://github.com/symbioticfi/relay) (`make build-relay-utils`).
Examples below are written as `utils …` for brevity.
:::

:::note
`relay_utils` is **not** the same tool as the [`symb` CLI](/integrate/builders-researchers/cli).
`symb` reads and writes Symbiotic **Core** state (registries, opt-ins, vault allocations);
`relay_utils` handles the relay-specific layer (validator keys, `KeyRegistry`, genesis). Operators
typically use both.
:::

## What you need

Every command below uses the same handful of values. Here is exactly what each one is and where to
get it:

| Value                               | What it is                                                                                              | Where to get it                                                                                                                                                                 |
| ----------------------------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `<ValSetDriver address>`            | The network's on-chain config contract — the single source of truth the CLI reads everything else from. | The network operator gives it to you. (After deployment it is written into the network's [`my-relay-deploy.toml`](/integrate/networks/relay-onchain#deployment).)               |
| `<driver_chain_id>`                 | Chain ID where `ValSetDriver` is deployed.                                                              | From the network operator, alongside the driver address.                                                                                                                        |
| `<vpp_chain_id>`                    | Chain ID where the network's `VotingPowerProvider` lives (often the same as the driver chain).          | From the network operator, or read it: `cast call <ValSetDriver> "getVotingPowerProviders()((uint64,address)[])" --rpc-url <rpc>`.                                              |
| `<rpc_url>` (and `<rpc1>,<rpc2>,…`) | A JSON-RPC endpoint for each chain you transact on (driver, VPP, settlement chains).                    | Your own node or a provider (e.g. Alchemy). Supported chains and Symbiotic Core addresses: [Addresses](/get-started/resources/addresses).                                       |
| `<tag>` (key tags)                  | Which validator keys the network requires — you generate and register one per tag.                      | Ask the network operator, or read on-chain: `cast call <ValSetDriver> "getRequiredKeyTags()(uint8[])" --rpc-url <rpc>` (the header-signing tag is `getRequiredHeaderKeyTag()`). |
| `<operator_private_key>`            | Your operator EOA key — signs the on-chain transactions and pays gas.                                   | Your own key. Fund the address with native gas on every chain you transact on.                                                                                                  |

:::note
You never pass the `VotingPowerProvider`, `KeyRegistry`, or `Settlement` addresses to `relay_utils`
directly — it resolves them from the `ValSetDriver` for you. The driver address + chain ID and an
RPC are enough.
:::

## Operator path

Joining an existing network, in order:

1. **Find the required key tags** — they live in the driver's config (`getRequiredKeyTags`); you generate **one relay key per tag** → [Generate and manage keys](#generate-and-manage-keys).
2. **Register each key** in `KeyRegistry` → [Register a key](#register-a-key-in-keyregistry).
3. **Register your operator** in the `VotingPowerProvider` → [Register as an operator](#register-as-an-operator). On auto-deploy networks this also creates your vault.
4. **Opt into your vault and deposit stake** — Core [opt-ins](/integrate/operators/opt-ins) + the curator's [allocation limits](/integrate/curators/manage-allocations) — so your voting power is active.
5. **Run the sidecar** → [Relay Off-Chain](/integrate/networks/relay-offchain), then confirm with [Check operator status](#check-operator-status).

## Generate and manage keys

Keys live in a local keystore (`--path`, default `./keystore.jks`, unlocked with `--password`).

Generate an EVM key for paying gas on a chain, or import an existing one:

```bash [bash]
# generate a fresh EVM key for the voting-power-provider chain
utils keys add --generate --evm --chain-id <vpp_chain_id> --password <pwd>

# or import an existing private key
utils keys add --evm --chain-id <vpp_chain_id> --private-key 0x... --password <pwd>
```

Generate one **relay** signing key per key tag the network requires (ask the network operator which
tags are required):

```bash [bash]
utils keys add --relay --key-tag <tag> --generate --password <pwd>
```

:::note
**Key tags.** A key tag is one byte: `(keyType << 4) | keyId`. Key types: `0` = BLS BN254 (the only
type that supports signature aggregation), `1` = ECDSA secp256k1, `2` = BLS12-381. So tag `15`
(`0x0F`) is BLS BN254 / id 15, and tag `16` (`0x10`) is ECDSA secp256k1 / id 0.
:::

Inspect and clean up:

```bash [bash]
utils keys list --password <pwd>
utils keys remove --evm --chain-id <vpp_chain_id> --password <pwd>
```

## Register a key in `KeyRegistry`

Each relay key must be registered on-chain so the network can attribute signatures to your operator.
Run once per required key tag:

```bash [bash]
utils operator register-key \
  --driver.address <ValSetDriver address> \
  --driver.chainid <driver_chain_id> \
  --voting-provider-chain-id <vpp_chain_id> \
  --chains <rpc_url> \
  --key-tag <tag> \
  --secret-keys <vpp_chain_id>:<operator_private_key> \
  --password <pwd>
```

This builds the proof of key ownership and calls `KeyRegistry.setKey(...)`. You don't pass the
`KeyRegistry` address — `relay_utils` resolves it from the `ValSetDriver`. To see it directly (e.g. to
inspect the contract on a block explorer), read it off the driver:

```bash [bash]
cast call <ValSetDriver address> "getKeysProvider()((uint64,address))" --rpc-url <rpc_url>
```

The returned `(chainId, address)` is the chain and address of the network's `KeyRegistry`.

:::warning
`register-key` operates on a **single chain** at a time, and reads `ValSetDriver` at the chain's
`finalized` block. On a freshly-started local devnet `finalized` is block `0`, so the call reverts
with `no contract code at given address` — advance it once (e.g. `cast rpc anvil_mine 100`) before
registering. Public testnets and mainnet are unaffected.
:::

## Register as an operator

Register your operator in the network's `VotingPowerProvider`. This adds you to the network's operator
set; once you also hold the required keys and stake, you become eligible for the derived validator set.
It calls `VotingPowerProvider.registerOperator()`:

```bash [bash]
utils operator register-operator \
  --driver.address <ValSetDriver address> \
  --driver.chainid <driver_chain_id> \
  --voting-provider-chain-id <vpp_chain_id> \
  --chains <rpc_url> \
  --secret-keys <vpp_chain_id>:<operator_private_key>
```

As with key registration, the `VotingPowerProvider` address is resolved from the `ValSetDriver` — you
don't pass it. To see it directly (e.g. to register through a Safe, or to look up your vault below):

```bash [bash]
cast call <ValSetDriver address> "getVotingPowerProviders()((uint64,address)[])" --rpc-url <rpc_url>
```

:::info
Prerequisite: the operator must already be registered in Symbiotic Core's `OperatorRegistry` and
opted into the network — see [Register Operator](/integrate/operators/register-operator) and
[Opt-Ins](/integrate/operators/opt-ins).
:::

:::note
If the network's `VotingPowerProvider` uses the `OpNetVaultAutoDeploy` extension, this call also
**auto-creates a vault** dedicated to your operator. Read its address from the transaction logs, or
look it up afterwards:

```bash [bash]
cast call <VotingPowerProvider address> "getOperatorVaults(address)(address[])" <operator_address> --rpc-url <rpc_url>
```

Opt into that vault and deposit stake into it. On networks without auto-deploy, opt into an existing
curator-managed vault instead.
:::

Verify the registration:

```bash [bash]
cast call <VotingPowerProvider address> "isOperatorRegistered(address)(bool)" <operator_address> --rpc-url <rpc_url>
```

To let a third party submit the transaction (gasless), generate an EIP-712 signature with
`utils operator register-operator-with-signature` and hand the artifact to the submitter.

:::warning
Registering (and even depositing) is not enough on its own — your stake only counts as **voting
power** once the vault's delegator limits are set. See
[Manage Allocations](/integrate/curators/manage-allocations) for who sets `setNetworkLimit` /
`setOperatorNetworkShares`.
:::

## Commit the genesis validator set

A network operator runs this **once** to bootstrap the network: it derives the first validator set
and commits the genesis header to every settlement chain. Until genesis is committed, sidecars have
no starting snapshot to agree on.

:::warning
Genesis derives the set from the voting power that exists **right now**. Make sure your initial
operators are registered in the `VotingPowerProvider`, have their keys in `KeyRegistry`, and are
backed by stake (with delegator limits set) **before** committing — otherwise the genesis validator
set is empty. The `--secret-keys` are the committer keys that sign the genesis transactions on each
chain.
:::

```bash [bash]
utils network generate-genesis \
  --driver.address <ValSetDriver address> \
  --driver.chainid <driver_chain_id> \
  --chains <rpc1>,<rpc2>,... \
  --secret-keys <chain_id>:<key>,... \
  --commit
```

Omit `--commit` for a dry run — it prints the derived header without sending transactions, which is
the fastest way to sanity-check your `ValSetDriver` configuration. Inspect the live network with:

```bash [bash]
utils network info \
  --driver.address <ValSetDriver address> \
  --driver.chainid <driver_chain_id> \
  --chains <rpc1>,<rpc2>,...
```

## Check operator status

Confirm your operator is registered, included, and carries voting power:

```bash [bash]
utils operator info \
  --driver.address <ValSetDriver address> \
  --driver.chainid <driver_chain_id> \
  --voting-provider-chain-id <vpp_chain_id> \
  --chains <rpc_url>
```

If you registered keys and deposited stake but still show zero voting power, the missing piece is
almost always a vault delegator limit — see [Manage Allocations](/integrate/curators/manage-allocations).

## Next Steps

<div
  style={{
      display: "grid",
      gridTemplateColumns: "repeat(auto-fit, minmax(300px, 1fr))",
      gap: "24px",
      marginBottom: "48px",
  }}
>
  <Card1
    title="Relay Off-Chain"
    description="Run the relay sidecar so your operator starts signing and aggregating."
    icon={
      <svg width="48" height="48" viewBox="0 0 48 48" fill="none">
          <rect x="8" y="8" width="32" height="32" rx="2" stroke="currentColor" strokeWidth="2" fill="none" />
          <rect x="12" y="12" width="24" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="18" width="20" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="24" width="16" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="30" width="18" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
      </svg>
  }
    href="/integrate/networks/relay-offchain"
  />

  <Card1
    title="Relay On-Chain"
    description="Deploy the Relay contracts your network and operators reference here."
    icon={
      <svg width="48" height="48" viewBox="0 0 48 48" fill="none">
          <rect x="8" y="8" width="32" height="32" rx="2" stroke="currentColor" strokeWidth="2" fill="none" />
          <rect x="12" y="12" width="24" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="18" width="20" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="24" width="16" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="30" width="18" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
      </svg>
  }
    href="/integrate/networks/relay-onchain"
  />
</div>


---

## /integrate/networks/rewards

---
description: "Networks use Rewards system to compensate Stakers, Operators, and Curators for providing security and network operation."
---

import { Card1 } from "../../../components/Card1";

# Distribute Rewards

Networks use Rewards system to compensate Stakers, Operators, and Curators for providing security and network operation.

This guide highlights practical steps and contract touchpoints to distribute and claim rewards.

To reproduce this flow, follow the [Quickstart: Network with Staking](/integrate/networks/minimal-network) guide first.

***

The rewards implementation is available in the [Symbiotic Rewards Repository](https://github.com/symbioticfi/rewards-v2). There are 3 smart contracts:

- **Rewards**: Main contract for distributions and claims.
- **FeeRegistry**: Registry for operator and curator fees.
- **CuratorRegistry**: Registry mapping vaults to curators.

## Vault Snapshot Rewards

![Network rewards distribution diagram](/img/rewards-fee-split.png)

Vault Snapshot Rewards implement **fully on-chain**, snapshot-based distributions using historical vault fee split and state at specific timestamps.

:::info
The distribution and claim operations must be handled separately for each vault.
:::

Actions ([Contract](https://github.com/symbioticfi/rewards-v2/blob/main/src/contracts/VaultSnapshotRewards.sol) | [Interface](https://github.com/symbioticfi/rewards-v2/blob/main/src/interfaces/IVaultSnapshotRewards.sol)):

- **Distribution entrypoint**: `distributeVaultSnapshotRewards()` - used by network to distribute rewards.
- **Claim entrypoints** for stakers, operators and curators:
  - `claimVaultSnapshotRewards()`
  - `claimOperatorFees()`
  - `claimCuratorFees()`

:::info
Both network and its middleware can distribute Vault Snapshot rewards.
:::

### Reward Workflow

:::info
Reward distribution is a regular routine. Unlike DeFi farms that distribute rewards continuously, networks distribute rewards periodically (for example, weekly).
:::

Rewards contract addresses are listed on the [Addresses](/get-started/resources/addresses#rewards) page.

:::steps
#### Distribute Rewards

For `msg.sender`, use the Network Middleware account (see [Quickstart: Network with Staking](/integrate/networks/minimal-network)).

- Increase token allowance  ([Sample Tx](https://hoodi.etherscan.io/tx/0x51b48ffe641da09b8975a3bc670b77b7dd1b62c1d6028770cc569bcbe4404987))
- Distribute rewards by calling `Rewards` via `distributeVaultSnapshotRewards()`  ([Sample Tx](https://hoodi.etherscan.io/tx/0xca0ce507a5e93230c848cf5a4b261714847500fef5203b27dbc49dcbe78b8d1a))

#### Verify on Symbiotic dApp

Open the [Vault Page](https://app.hoodi.symbiotic.fi/vault/0xb29BAD40B00587dE1145C7862F96746f043b9daD). Find Vault Reward data and claimed rewards in the corresponding section.

#### Staker Flow

- [Acquire wstETH](https://stake-hoodi.testnet.fi/wrap)  ([Sample Tx](https://hoodi.etherscan.io/tx/0xe799b1611a19cc4ad9c8bc1d61cd328c1f54218c78a5b18f9b559c8791d5b876))
- Deposit to Vault (via UI) ([Tx 1](https://hoodi.etherscan.io/tx/0xe622a7bfd0c17e76f92da77a8ce378aebf5e32443e2fb4f78363871efab51531), [Tx 2](https://hoodi.etherscan.io/tx/0x0ebe2fedef0443d38ee7e933600b3f16139d87d31dbed627cd9fe314effddb33))
- Claim Rewards via `claimVaultSnapshotRewards` ([Sample Tx](https://hoodi.etherscan.io/tx/0x0f9d5d14772999320b88b45ad40f6764054a56993386f0d0d709ff7a2fcd7c8e))

#### Curator and Operator flows

- The `vault.owner()` registers the curator in `CuratorRegistry`
- Curator sets curator and operator fees in `FeeRegistry`.
- Curator and operator claim via `Rewards.sol`

For more details, see [Curator: Manage & Claim Fees](/integrate/curators/registry-and-fees#claim-process) and [Operator: Claim Rewards](/integrate/operators/claim-rewards).
:::

### Production considerations

This guide uses testnet and simplified EOAs for ease of replication. In production, plan the security-aware setup accordingly.

- [Implement Middleware](/integrate/networks/network-contract), do not use EOA or multisig
- Expect fees to be deducted from the amount distributed. To perform fee-related calculations, use [FeeRegistry and Rewards methods](/integrate/curators/registry-and-fees#fee-related-calculations)

## Next Steps

<div
  style={{
      display: "grid",
      gridTemplateColumns: "repeat(auto-fit, minmax(300px, 1fr))",
      gap: "24px",
      marginBottom: "48px",
  }}
>
  <Card1
    title="Slashing"
    description="Learn how to integrate the slashing mechanism into your system if needed."
    icon={
      <svg width="48" height="48" viewBox="0 0 48 48" fill="none">
          <rect
              x="8"
              y="8"
              width="32"
              height="32"
              rx="2"
              stroke="currentColor"
              strokeWidth="2"
              fill="none"
          />
          <rect x="12" y="12" width="24" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="18" width="20" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="24" width="16" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="30" width="18" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
      </svg>
  }
    href="/integrate/networks/slashing"
  />

  <Card1
    title="Relay On-Chain"
    description="Proceed to developing your protocol's on-chain components using Relay smart contracts."
    icon={
      <svg width="48" height="48" viewBox="0 0 48 48" fill="none">
          <rect
              x="8"
              y="8"
              width="32"
              height="32"
              rx="2"
              stroke="currentColor"
              strokeWidth="2"
              fill="none"
          />
          <rect x="12" y="12" width="24" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="18" width="20" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="24" width="16" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="30" width="18" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
      </svg>
  }
    href="/integrate/networks/relay-onchain"
  />
</div>


---

## /integrate/networks/slashing

---
description: "Integrate Symbiotic slashing flows, resolvers, veto slashers, burners, and operator accountability checks."
---

import { Card1 } from "../../../components/Card1";

# Slashing

[Read Learn first](/learn/core-concepts/slashing)

Slashing is a penalty mechanism that deters operators from breaking their commitments. Violations include failing to complete tasks properly or accurately. Slashing typically burns or redistributes the operator's staked funds.

## **Slasher Module**

Each Symbiotic Vault has an immutably set `Slasher` module implementation. There are three possible implementation choices:

1. **No Slasher** - no slashing can occur within the Vault

2. **Instant Slasher (`TYPE = 0`)** - allows networks to immediately slash funds in a FIFO order

3. **Veto Slasher (`TYPE = 1`)** - supports a veto process over a pre-defined veto period, where designated **Resolvers** can cancel the slashing request

Each Slasher module contains a `slashableStake(subnetwork, operator, captureTimestamp, hints)` function that returns the amount of collateral still slashable for the given `captureTimestamp` at the current moment.

:::warning
The provided slashable collateral amount may be inaccurate in practice when the vault uses **restaking** (multiple networks may slash the same amount of collateral).
:::

:::info
When executing a slashing request, funds are transferred to an immutably set `Vault.burner()`.
:::

### Slashing Guarantees

Each Symbiotic Vault has an epoch duration (obtained via `Vault.epochDuration()`), which determines the withdrawal delay and provides a period during which slashing guarantees are held.

The following inequality must hold:

<div align="center">
  <code>executeSlashTimestamp - captureTimestamp \<= epochDuration</code>
</div>

### Slasher (Type 0)

The slasher instantly executes slashing requests when received and validated.

```solidity [NetworkSlasher.sol]
import {IVault} from "@symbioticfi/core/src/interfaces/vault/IVault.sol";
import {ISlasher} from "@symbioticfi/core/src/interfaces/slasher/ISlasher.sol";
import {Subnetwork} from "@symbioticfi/core/src/contracts/libraries/Subnetwork.sol";

address slasher = IVault(vault).slasher();
bytes32 subnetwork = Subnetwork.subnetwork(NETWORK, IDENTIFIER);
ISlasher(slasher).slash(
    subnetwork,
    operator,
    amount,
    captureTimestamp,
    hints
)
```

Parameters:

- `subnetwork` - full identifier of the subnetwork (address of the network concatenated with the uint96 identifier)
- `operator` - address of the operator
- `amount` - amount of the collateral to slash
- `captureTimestamp` - time point when the stake was captured
- `hints` - hints for checkpoints' indexes

:::info
Call this function from `NetworkMiddlewareService.middleware(network)`.
:::

### VetoSlasher (Type 1)

The flow consists of three stages:

1. Request Slashing
2. Veto Slashing
3. Execute Slashing (if not vetoed)

Let’s assume the veto duration period is set to **5 days** and the epoch duration is set to **7 days**.

::::steps
#### Day 1 - Request Slashing

```solidity [NetworkSlasher.sol]
import {IVault} from "@symbioticfi/core/src/interfaces/vault/IVault.sol";
import {IVetoSlasher} from "@symbioticfi/core/src/interfaces/slasher/IVetoSlasher.sol";
import {Subnetwork} from "@symbioticfi/core/src/contracts/libraries/Subnetwork.sol";

address slasher = IVault(vault).slasher();
bytes32 subnetwork = Subnetwork.subnetwork(NETWORK, IDENTIFIER);
uint256 slashIndex = IVetoSlasher(slasher).requestSlash(
    subnetwork,
    operator,
    amount,
    captureTimestamp,
    hints
)
```

This call succeeds only if the following inequality holds:

<div align="center">
  <code>requestSlashTimestamp + vetoDuration - captureTimestamp \<= epochDuration</code>
</div>

:::info
`NetworkMiddlewareService.middleware(network)` should call this function.
:::

#### Days 1 to 5 - Veto Slashing

```solidity [Resolver]
IVetoSlasher(slasher).vetoSlash(slashIndex, hints)
```

:::info
Call this function from `VetoSlasher.resolver(subnetwork, hint)`.
:::

#### Days 6 to 7 - Execute Slashing

If the slashing request wasn't vetoed:

```solidity [NetworkSlasher.sol]
IVetoSlasher(slasher).executeSlash(
    slashIndex,
    hints
)
```

:::info
`NetworkMiddlewareService.middleware(network)` should call this function.
:::
::::

## Resolvers

If a vault uses a VetoSlasher, there is a veto phase (duration set during vault deployment) when the resolver can veto the request.

Networks can set the resolver via `IVetoSlasher(slasher).setResolver(identifier, resolver, hints)`.

:::note
The first time you set the resolver, it applies immediately. Otherwise, the update applies after `VetoSlasher.resolverSetEpochsDelay()` epochs.
:::

## Next Steps

<div
  style={{
      display: "grid",
      gridTemplateColumns: "repeat(auto-fit, minmax(300px, 1fr))",
      gap: "24px",
      marginBottom: "48px",
  }}
>
  <Card1
    title="Relay On-Chain"
    description="Proceed to developing your protocol's on-chain components using Relay smart contracts."
    icon={
          <svg width="48" height="48" viewBox="0 0 48 48" fill="none">
              <rect
                  x="8"
                  y="8"
                  width="32"
                  height="32"
                  rx="2"
                  stroke="currentColor"
                  strokeWidth="2"
                  fill="none"
              />
              <rect
                  x="12"
                  y="12"
                  width="24"
                  height="3"
                  rx="1.5"
                  fill="currentColor"
                  opacity="0.3"
              />
              <rect
                  x="12"
                  y="18"
                  width="20"
                  height="3"
                  rx="1.5"
                  fill="currentColor"
                  opacity="0.3"
              />
              <rect
                  x="12"
                  y="24"
                  width="16"
                  height="3"
                  rx="1.5"
                  fill="currentColor"
                  opacity="0.3"
              />
              <rect
                  x="12"
                  y="30"
                  width="18"
                  height="3"
                  rx="1.5"
                  fill="currentColor"
                  opacity="0.3"
              />
          </svg>
      }
    href="/integrate/networks/relay-onchain"
  />
</div>


---

## /integrate/networks/submit-metadata

---
description: "The Symbiotic UI displays TVL, allocations, and relationships between curators, vaults, operators, and networks. To make your entity visible on the UI, submit..."
---

import { Card1 } from "../../../components/Card1";

# Submit Metadata

The [Symbiotic UI](https://app.symbiotic.fi/deposit) displays TVL, allocations, and relationships between curators, vaults, operators, and networks. To make your entity visible on the UI, submit its metadata to the corresponding repository.

After you submit metadata, the Symbiotic team reviews and merges it. Once merged, your data appears on the UI.

## Add a New Entity Template

### Choose a Repository

| Chain   | URL                                                                                                |
| ------- | -------------------------------------------------------------------------------------------------- |
| Mainnet | [https://github.com/symbioticfi/metadata-mainnet](https://github.com/symbioticfi/metadata-mainnet) |
| Hoodi   | [https://github.com/symbioticfi/metadata-hoodi](https://github.com/symbioticfi/metadata-hoodi)     |

### Repository Structure

The repository is organized as follows:

```
repository/
├── vaults/
│   ├── 0x<address>/
│   │   ├── info.json
│   │   └── logo.png (optional)
├── networks/
├── operators/
├── tokens/
```

Each entity is identified by its Ethereum address (`0x...`), and its data is stored in a folder named after the address. Inside this folder, include a file `info.json` containing metadata, and optionally, an icon file `logo.png`.

***

### Steps to Add a New Entity

**Note: After your PR is submitted, email your PR link to [verify@symbiotic.fi](mailto\:verify@symbiotic.fi) from your official business email (domain must match that of your entity website) to allow us to confirm your identity ahead of merging your PR.**

1. **Determine the entity type**:
   - Decide whether the entity belongs to `vaults`, `networks`, `operators`, `tokens` or `points`.
   - If the entity is a `vault`, ensure its collateral token entity is registered in the `tokens` folder before adding the vault metadata. If not, add the token first.
2. **Register the entity in the registry**:
   - Before adding metadata for vaults, networks, or operators, ensure that they are registered in their respective registries. You can find the current registry contract addresses in the [Addresses page](/get-started/resources/addresses). Unregistered entities will not be accepted.
3. **Create a new folder**:
   - Navigate to the appropriate directory for the entity type.
   - Create a folder named after the Ethereum address (e.g., `0x1234567890abcdef1234567890abcdef12345678`).
4. **Add the `info.json` file**:
   - Include metadata in the specified format (see below).
5. **(Optional) Add an icon file**:
   - If available, include a `logo.png` file with the entity’s logo.

The Symbiotic team reviews your PR after automated checks pass. If approved, it will be merged into the repository.

***

### File Format: `info.json`

The `info.json` file must follow this structure:

#### Required Fields

- `name` (string): The name of the entity.
- `description` (string): A brief description of the entity.
- `tags` (array of strings): Tags categorizing the entity.
- `links` (array of objects): External links related to the entity.

#### Fields for Points

- `type` (string): The type of the point (e.g., "network").
- `decimals` (number): The number of decimal places for the point.

#### Supported `links` Types

Each link should include:

- `type`: The type of the link. Supported values are:
  - `website`: The official website of the entity.
  - `explorer`: A blockchain explorer link for the entity's Ethereum address or contract.
  - `docs`: Documentation related to the entity.
  - `example`: Example use cases or tutorials.
  - `externalLink`: A link to be shown below the entity's name.
- `name`: A user-friendly name for the link.
- `url`: The URL of the resource.

### Icon File: `logo.png` (Optional)

If you want to include an icon for the entity, follow these guidelines:

- **File Name**: `logo.png`
- **Dimensions**: 256x256 pixels
- **Format**: PNG

Place the `logo.png` file in the same folder as the `info.json` file.

***

### Validation

Before submitting your PR, ensure the following:

1. The Ethereum address is valid:
   - It must start with `0x` and be exactly 42 characters long.
2. The `info.json` file is valid:
   - Use a JSON validator, such as [https://jsonlint.com/](https://jsonlint.com/).
3. The `logo.png` file (if included) meets the size requirement of **256x256 pixels**.

***

### Submitting the Pull Request

Once your files are added to the repository, create a Pull Request with the following details:

1. **Entity Type**: Specify the type (vault, network, operator, token).
2. **Ethereum Address**: Provide the address of the entity.
3. **Description**: Summarize the entity’s purpose and data.

#### Example PR Description

```
Added new token entity: 0x1234567890abcdef1234567890abcdef12345678

- **Name**: USDT
- **Description**: USDT is a stablecoin pegged to the US Dollar, widely used for trading and liquidity in cryptocurrency markets.
- **Tags**: stablecoin, usdt
- **Links**:
  - [Website](https://tether.to/)
  - [Etherscan](https://etherscan.io/token/0xdac17f958d2ee523a2206206994597c13d831ec7)
  - [Tether Documentation](https://docs.tether.to/)
- **CMC ID**: 825
- **Permit Name**: USDT Permit Token
- **Permit Version**: 1
- **Icon**: Included (256x256 px)
```

***

### Review and Approval

Your PR will be reviewed to ensure:

- The `info.json` file has all required fields and valid data.
- The `logo.png` file (if included) meets the requirements.
- The metadata is accurate and well-structured.
- The submitter of the PR is from the entity in question (verified via an email with your PR link to [verify@symbiotic.fi](mailto\:verify@symbiotic.fi) from your official business email)

After approval, your changes will be merged into the repository.

## Add a Network

:::steps
##### Create a new folder in the `/networks` directory

##### Create a new json file in the folder with the following structure:

```json [info.json]
{
    "name": "My Network",
    "description": "My Network is a network that allows you to stake your tokens and earn rewards.",
    "tags": ["network", "staking"],
    "links": [{ "type": "website", "name": "Website", "url": "https://mynetwork.com" }]
}
```

##### Save a logo of the Network to `logo.png` of 256x256 pixels size
:::

## Add Points

:::steps
##### Create a new folder in the `/points` directory

##### Create a new json file in the folder with the following structure:

```json [info.json]
{
    "name": "My Points",
    "description": "My Points is a points system that allows you to earn rewards.",
    "tags": ["points", "staking"],
    "links": [{ "type": "website", "name": "Website", "url": "https://mypoints.com" }]
}
```

##### Save a logo of the Points to `logo.png` of 256x256 pixels size
:::

## Add a Pre-deposit Vault

See the [vault metadata submission guide](/integrate/curators/submit-metadata) for details.

## Next Steps

<div
  style={{
      display: "grid",
      gridTemplateColumns: "repeat(auto-fit, minmax(300px, 1fr))",
      gap: "24px",
      marginBottom: "48px",
  }}
>
  <Card1
    title="Deploy a Pre-deposit Vault"
    description="Use this guide if you need to curate or deploy the Vault by yourself."
    icon={
      <svg width="48" height="48" viewBox="0 0 48 48" fill="none">
          <rect
              x="8"
              y="8"
              width="32"
              height="32"
              rx="2"
              stroke="currentColor"
              strokeWidth="2"
              fill="none"
          />
          <rect x="12" y="12" width="24" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="18" width="20" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="24" width="16" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="30" width="18" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
      </svg>
  }
    href="/integrate/networks/pre-deposit"
  />

  <Card1
    title="Rewards"
    description="Learn how to integrate Symbiotic rewards into your system."
    icon={
      <svg width="48" height="48" viewBox="0 0 48 48" fill="none">
          <rect
              x="8"
              y="8"
              width="32"
              height="32"
              rx="2"
              stroke="currentColor"
              strokeWidth="2"
              fill="none"
          />
          <rect x="12" y="12" width="24" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="18" width="20" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="24" width="16" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
          <rect x="12" y="30" width="18" height="3" rx="1.5" fill="currentColor" opacity="0.3" />
      </svg>
  }
    href="/integrate/networks/rewards"
  />
</div>

