Skip to content
LogoLogo

Relay Quickstart

This quickstart walks through the developer workflow that the repo enables: spinning up Symbiotic Core locally, preparing vaults and operators, wiring middleware, and bringing a relay-powered network online.

After completing this guide, you will learn how to:

  1. Spin up Symbiotic Core locally
  2. Set up test Vaults and Operators
  3. Configure Your Relay-backed Network's Smart Contracts
  4. Bring Your Network Online
Symbiotic Super Sum project structure
symbiotic-super-sum
├─ network-scripts
│  ├─ deploy.sh
│  ├─ genesis-generator.sh
│  ├─ sidecar-start.sh
│  └─ sum-node-start.sh
├─ off-chain
│  ├─ abis/
│  ├─ cmd/
│  │  ├─ benchmark/
│  │  └─ node/
│  │     └─ main.go
│  └─ internal/
│     ├─ contracts/
│     │  └─ sumTask.go
│     └─ utils/
├─ script
│  ├─ MyRelayDeploy.sol
│  ├─ mocks/
│  ├─ my-relay-deploy.toml
│  └─ utils/
├─ src
│  ├─ SumTask.sol
│  └─ symbiotic
│     ├─ Driver.sol
│     ├─ KeyRegistry.sol
│     ├─ Settlement.sol
│     └─ VotingPowers.sol
├─ generate_network.sh
├─ package.json
└─ …

Prerequisites

Make sure to have all the tools listed below installed before you start.

  1. Git (Installation Guide)
  2. Foundry - Ethereum development toolchain (Installation Guide)
  3. Node.js (v18 or later) and npm (Installation Guide)
  4. Docker (Installation Guide)
  5. Go (v1.21 or later) (Installation Guide)
  6. Python (v3.11 or later) (Installation Guide)

1. Deploy Symbiotic Core and Setup Your First Vault

core-and-vault.png

Deploy Symbiotic Core to your local chain

Symbiotic Core comprises the foundational contracts that govern the Symbiotic ecosystem, offering a comprehensive suite for its administration and interaction. The repository provides pre-configured scripts to facilitate deployment to a local chain, as well as additional utilities designed to streamline ongoing workflows with the core contracts.

Core deployment

SymbioticCoreInit.sol is the integration script that spins up fresh factories, registries, vaults, and helper services, then exposes utilities for building test scenarios. SymbioticCoreConstants.sol is the address book library that returns the canonical contract set and supported collateral tokens for mainnet, Holešky, Sepolia, and Hoodi. SymbioticCoreBindings.sol contains the Foundry broadcast helpers used to create vaults, register operators/networks, manage opt-ins, and exercise slashing flows against those core contracts.

Core contracts can be deployed using _initCore_SymbioticCore function (script details):

@symbioticfi/relay-contracts/script/deploy/RelayDeploy.sol#101
function getCore() public withoutBroadcast loadConfig returns (SymbioticCoreConstants.Core memory) {
    if (!SymbioticCoreConstants.coreSupported()) {
        if (config.get("vault_factory").data.length == 0) {
            SymbioticCoreConstants.Core memory core = _initCore_SymbioticCore(false); 

The console output lists every deployed Symbiotic Core contract (vault factory, registries, opt-in services, etc.). Persist these addresses—you’ll feed them into the rest of the quickstart.

Deploy a vault

Vaults are the delegation and restaking management layer of Symbiotic. They handle three crucial parts of the Symbiotic economy: accounting, delegation strategies and slashing processing

Vault deployment

The vault is created during operator registration by VotingPowers contract (see contract details).

@symbioticfi/relay-contracts/src/modules/voting-power/extensions/logic/OpNetVaultAutoDeployLogic.sol#289
 function createVault(
     uint64 version,
     address owner,
     bytes memory vaultParams,
     uint64 delegatorIndex,
     bytes memory delegatorParams,
     bool withSlasher,
     uint64 slasherIndex,
     bytes memory slasherParams
 ) public returns (address, address, address) {
     return IVaultConfigurator(IOpNetVaultAutoDeploy(address(this)).VAULT_CONFIGURATOR())
         .create(
             IVaultConfigurator.InitParams({
                 version: version,
                 owner: owner,
                 vaultParams: vaultParams,
                 delegatorIndex: delegatorIndex,
                 delegatorParams: delegatorParams,
                 withSlasher: withSlasher,
                 slasherIndex: slasherIndex,
                 slasherParams: slasherParams
             })
         );
 }

For testnet or mainnet vaults you can skip this step and create one directly via the Vault Factory UI at https://app.symbiotic.fi/create

2. Opt-Ins and Stake Allocation

operator-opt-in.png

Required On-Chain Actions

Deploy network

In Symbiotic, networks are represented through a network address (either an EOA or a contract) and a middleware contract. Deploy the network contract using the DeployNetworkBase script. During initialization, the network is also registered within NetworkRegistry (script details).

script/MyRelayDeploy.sol#101
function getNetwork() public withoutBroadcast loadConfig returns (address) {
    if (config.get("network").data.length == 0) {
        address[] memory proposersAndExecutors = new address[](1);
        proposersAndExecutors[0] = getDeployerAddress();
        SymbioticCoreConstants.Core memory core = getCore();
        vm.broadcast();
        address networkImpl =
            address(new Network(address(core.networkRegistry), address(core.networkMiddlewareService)));
        config.set(
            "network",
            _deployContract(
                NETWORK_SALT,
                networkImpl,
                    abi.encodeCall(
                        INetwork.initialize,
                        (INetwork.NetworkInitParams({
                                globalMinDelay: 0,
                                delayParams: new INetwork.DelayParams[](0),
                                proposers: proposersAndExecutors,
                                executors: proposersAndExecutors,
                                name: "Example Network",
                                metadataURI: "https://example.network",
                                defaultAdminRoleHolder: getDeployerAddress(),
                                nameUpdateRoleHolder: getDeployerAddress(),
                                metadataURIUpdateRoleHolder: getDeployerAddress()
                            }))
                    ),
                    getDeployerAddress(),
                    false
                )
            );
        }
        return config.get("network").toAddress();
    }
Register operator

The OperatorRegistry maintains a record of all registered operators. Operators must register here before they can participate in network activities or receive stake allocations (script details).

script/MyRelayDeploy.sol#371
getCore().operatorRegistry.registerOperator();
Operators opt into both the network and the vault

Opt-ins are crucial for establishing connections between different entities in the Symbiotic ecosystem. Through the OperatorNetworkOptInService, operators can opt into networks they wish to work with. This signifies their willingness to provide services to these networks. Operators use the OperatorVaultOptInService to opt into specific vaults. This allows them to receive stake allocations from these vaults (script details).

script/MyRelayDeploy.sol#372
getCore().operatorNetworkOptInService.optIn(address(getNetwork())); 
votingPowers.registerOperator();
IVault vault = IVault(votingPowers.getAutoDeployedVault(operator.addr));
getCore().operatorVaultOptInService.optIn(address(vault)); 
Curator configures network limits and allocations

Network opt into desired vaults by calling setMaxNetworkLimit() on each vault’s delegator contract (script details).

@symbioticfi/relay-contracts/src/modules/voting-power/extensions/OpNetVaultAutoDeploy.sol#69
(address vault, address delegator,) = OpNetVaultAutoDeployLogic.createVault(operator);
_registerOperatorVault(operator, vault);
if (isSetMaxNetworkLimitHookEnabled()) {
    ISetMaxNetworkLimitHook(NETWORK())
        .setMaxNetworkLimit(delegator, SUBNETWORK_IDENTIFIER(), type(uint256).max); 
  }

3. Network Middleware

middleware-flow.png

Overview

Relay Contracts use a modular architecture with five core modules that together manage validator networks, ensuring flexibility and clear separation of concerns.

  • Network integrates Relay Contracts into the Symbiotic ecosystem, providing verifiable delays, standardized lifecycle management, and serving as the network address across the system.
  • VotingPowerProvider connects to Symbiotic Core to calculate operator and vault voting power based on stake and rules. It supports extensible strategies for onboarding, slashing, rewards, and key management.
  • KeyRegistry manages and verifies operators’ cryptographic keys (BLS BN254, ECDSA SECP256K1), supporting registration, verification, and lifecycle management.
  • ValSetDriver derives and maintains validator sets for off-chain components, managing epoch transitions and linking on-chain voting power with off-chain consensus.
  • Settlement commits compressed validator sets each epoch, verifies signatures, and supports multi-chain deployments for cross-chain validation.
Middleware deployment

Deployment tooling

The deployment tooling is in the @symbioticfi/relay-contracts/script/deploy/ folder. It consists of RelayDeploy.sol Foundry script template and 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 - abstract base that wires common Symbiotic Core helpers and exposes the four deployment hooks: KeyRegistry, VotingPowerProvider, Settlement, and ValVetDriver
  • relay-deploy.sh - orchestrates per-contract multi-chain deployments

The script deploys Relay modules under OpenZeppelin's TransparentUpgradeableProxy using CreateX (it provides better control for production deployments and more simplified approaches for development).

Deployment

  1. Implement your MyRelayDeploy.sol Foundry script with the deployment configuration of your Relay modules:

    • Implement all virtual functions of RelayDeploy.sol
    • In the constructor, take the path of the toml file
    • Use additional helpers such as getCore(), getKeyRegistry(), getVotingPowerProvider(), etc. (see full list in RelayDeploy.sol)
  2. Implement your my-relay-deploy.toml configuration file with RPC URLs needed for deployment and specify which modules to deploy on which chains

  3. Execute the deployment script, e.g.:

    ./lib/relay-contracts-new/script/deploy/relay-deploy.sh ./script/MyRelayDeploy.sol "$DEPLOY_CONFIG_PATH" --broadcast --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80

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

Middleware contract

Networks need a middleware contract that incorporates custom logic and must include slashing logic. The script sets middleware for the network in the following way (script details)

script/MyRelayDeploy.sol#291
 Network(payable(network))
    .execute(
        address(core.networkMiddlewareService),
        0,
        abi.encodeWithSelector(INetworkMiddlewareService.setMiddleware.selector, votingPowerProvider),
        bytes32(0),
        bytes32(0)
    );

4. Network Go Live

relay-sidecar.png

The Symbiotic Relay is a peer-to-peer sidecar network that runs alongside main blockchain nodes using a stateless design based entirely on on-chain state. A built-in HTTP API allows querying validator data, tracking epochs, and managing quorum signature aggregation.

Symbiotic-super-sum spins up a mini Symbiotic relay network on your laptop: two local blockchains, a contract deployer, and as many operator nodes as you request. Once the contracts are live, a small genesis job hands every relay node the same starting snapshot so they agree on history. Each operator runs a relay sidecar that keeps them chatting over libp2p, watches both blockchains, and exposes a simple API. A matching sum node sits beside each relay, grabs the contract addresses, and handles the actual task submissions or aggregation work. Together, these containers mimic the full relay network

Running network

generate_network.sh generates Docker configuration. Start the network by running docker compose --project-directory temp-network up -d

Integration to sidecar example

There are several implementations of relay client - ts, rust and go. Here is Go example of integration to sidecar network (script details):

off-chain/cmd/node/main.go#315
relayClient = v1.NewSymbioticClient(conn)
...
suggestedEpoch := uint64(0)
epochInfos, err := relayClient.GetLastAllCommitted(ctx, &v1.GetLastAllCommittedRequest{}) 
if err != nil {
  return err
} else {
  for _, info := range epochInfos.EpochInfos {
    if suggestedEpoch == 0 || info.GetLastCommittedEpoch() < suggestedEpoch {
      suggestedEpoch = info.GetLastCommittedEpoch()
    }
  }
}
 
resp, err := relayClient.SignMessage(ctx, &v1.SignMessageRequest{  
  KeyTag:        15,
  Message:       msg,
  RequiredEpoch: &suggestedEpoch,
})
if err != nil {
  return err
}

Another example for cosmos-sdk can be found here.