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:
- Spin up Symbiotic Core locally
- Set up test Vaults and Operators
- Configure Your Relay-backed Network's Smart Contracts
- 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.
- Git (Installation Guide)
- Foundry - Ethereum development toolchain (Installation Guide)
- Node.js (v18 or later) and npm (Installation Guide)
- Docker (Installation Guide)
- Go (v1.21 or later) (Installation Guide)
- Python (v3.11 or later) (Installation Guide)
1. Deploy Symbiotic Core and Setup Your First Vault

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):
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).
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

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).
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).
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).
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).
(address vault, address delegator,) = OpNetVaultAutoDeployLogic.createVault(operator);
_registerOperatorVault(operator, vault);
if (isSetMaxNetworkLimitHookEnabled()) {
ISetMaxNetworkLimitHook(NETWORK())
.setMaxNetworkLimit(delegator, SUBNETWORK_IDENTIFIER(), type(uint256).max);
}3. Network Middleware

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 ValVetDriverrelay-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
-
Implement your
MyRelayDeploy.solFoundry script with the deployment configuration of your Relay modules:- Implement all virtual functions of
RelayDeploy.sol - In the constructor, take the path of the
tomlfile - Use additional helpers such as
getCore(),getKeyRegistry(),getVotingPowerProvider(), etc. (see full list in RelayDeploy.sol)
- Implement all virtual functions of
-
Implement your
my-relay-deploy.tomlconfiguration file with RPC URLs needed for deployment and specify which modules to deploy on which chains -
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)
Network(payable(network))
.execute(
address(core.networkMiddlewareService),
0,
abi.encodeWithSelector(INetworkMiddlewareService.setMiddleware.selector, votingPowerProvider),
bytes32(0),
bytes32(0)
);4. Network Go Live

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):
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.
