Common Addresses
- BurnerRouterFactory:
0x32e2AfbdAffB1e675898ABA75868d92eE1E68f3b
- wstETH:
0xB82381A3fBD3FaFA77B3a7bE693342618240067b
- wstETH global burner:
0x58D347334A5E6bDE7279696abE59a11873294FA4
- VaultConfigurator:
0xD2191FE92987171691d552C219b8caEf186eb9cA
- Network:
0x683Ed12e213a6D78C338D4e18440C42bb936085A
- NetworkMiddleware:
0x9403942D5B9EbC1D2cF9Ff0960b688d64588B094
- DefaultStakerRewardsFactory:
0xE6381EDA7444672da17Cd859e442aFFcE7e170F0
- NetworkOptInService:
0x58973d16FFA900D11fC22e5e2B6840d9f7e13401
- VaultOptInService:
0x95CC0a052ae33941877c9619835A233D21D57351
- OperatorRegistry:
0x6F75a4ffF97326A00e52662d82EA4FdE86a2C548
- BurnerRouterFactory:
0x32e2AfbdAffB1e675898ABA75868d92eE1E68f3b
- wstETH:
0xB82381A3fBD3FaFA77B3a7bE693342618240067b
- wstETH global burner:
0x58D347334A5E6bDE7279696abE59a11873294FA4
- VaultConfigurator:
0xD2191FE92987171691d552C219b8caEf186eb9cA
- Network:
0x683Ed12e213a6D78C338D4e18440C42bb936085A
- NetworkMiddleware:
0x9403942D5B9EbC1D2cF9Ff0960b688d64588B094
- DefaultStakerRewardsFactory:
0xE6381EDA7444672da17Cd859e442aFFcE7e170F0
- NetworkOptInService:
0x58973d16FFA900D11fC22e5e2B6840d9f7e13401
- VaultOptInService:
0x95CC0a052ae33941877c9619835A233D21D57351
- OperatorRegistry:
0x6F75a4ffF97326A00e52662d82EA4FdE86a2C548
- BurnerRouterFactory:
0x99F2B89fB3C363fBafD8d826E5AA77b28bAB70a0
- wstETH:
0xC329400492c6ff2438472D4651Ad17389fCb843a
- wstETH global burner:
0xdCaC890b14121FD5D925E2589017Be68C2B5B324
- VaultConfigurator:
0x29300b1d3150B4E2b12fE80BE72f365E200441EC
- Network:
0x8560C667Ae72F28D09465B342A480daB28821f6b
- NetworkMiddleware: “
- DefaultStakerRewardsFactory:
0xFEB871581C2ab2e1EEe6f7dDC7e6246cFa087A23
- NetworkOptInService:
0x7133415b33B438843D581013f98A08704316633c
- VaultOptInService:
0xb361894bC06cbBA7Ea8098BF0e32EB1906A5F891
- OperatorRegistry:
0xAd817a6Bc954F678451A71363f04150FDD81Af9F
Setup
The following setup steps will reference various Symbiotic core contracts. Refer to their deployments page
and core source code.
Prerequisites
For technical users who’re comfortable running Foundry scripts, we’ve provided example code for every step in the setup process.
→ Setup Scripts
Alternatively the Symbiotic CLI can assist in many of these steps. Refer to their docs.
Docs:
Tooling:
git clone
- Git — install
forge script, cast
- Foundry — install
python3 symb.py
- Symbiotic CLI — install
Vault Configuration
The Vault configuration process consists of the following substeps:
Deploy Burner Router
The Burner Router defines where slashed collateral is sent (burn address, treasury, etc.).
Every Vault must point to a Burner Router, so we deploy this first.
For the official Symbiotic approach, see: Symbiotic Burner Router Deployment Guide
# Clone ditto-contracts repository
git clone https://github.com/dittonetwork/ditto-contracts
cd ditto-contracts
# Define parameters
OWNER=<owner address>
BURNER_ROUTER_FACTORY=0x32e2AfbdAffB1e675898ABA75868d92eE1E68f3b
COLLATERAL=0xB82381A3fBD3FaFA77B3a7bE693342618240067b # wstETH
DELAY=$((2*24*60*60)) # 2 days
GLOBAL_RECEIVER=<treasury address>
# For the array parameters, format exactly as shown
NETWORK_RECEIVERS="[(<network address>,<receiver address>)]"
OPERATOR_NETWORK_RECEIVERS="[(<network address>,<operator address>,<receiver address>)]"
# Execute the script
forge script script/operator/01_BurnerRouterSetup.s.sol:BurnerRouterSetup \
--sig "run(address,address,address,uint48,address,(address,address)[],(address,address,address)[])" \
$OWNER $BURNER_ROUTER_FACTORY $COLLATERAL $DELAY $GLOBAL_RECEIVER \
"$NETWORK_RECEIVERS" "$OPERATOR_NETWORK_RECEIVERS" \
--rpc-url https://ethereum-sepolia-rpc.publicnode.com \
--chain sepolia \
--private-key <your-private-key> \
--etherscan-api-key <etherscan api key> \
--verify --broadcast
# Define parameters
OWNER=<owner address>
BURNER_ROUTER_FACTORY=0x32e2AfbdAffB1e675898ABA75868d92eE1E68f3b
COLLATERAL=0xB82381A3fBD3FaFA77B3a7bE693342618240067b # wstETH
DELAY=$((2*24*60*60)) # 2 days
GLOBAL_RECEIVER=<treasury address>
# For the array parameters, format exactly as shown
NETWORK_RECEIVERS="[(<network address>,<receiver address>)]"
OPERATOR_NETWORK_RECEIVERS="[(<network address>,<operator address>,<receiver address>)]"
# Execute the script
forge script script/operator/01_BurnerRouterSetup.s.sol:BurnerRouterSetup \
--sig "run(address,address,address,uint48,address,(address,address)[],(address,address,address)[])" \
$OWNER $BURNER_ROUTER_FACTORY $COLLATERAL $DELAY $GLOBAL_RECEIVER \
"$NETWORK_RECEIVERS" "$OPERATOR_NETWORK_RECEIVERS" \
--rpc-url https://ethereum-sepolia-rpc.publicnode.com \
--chain sepolia \
--private-key <your-private-key> \
--etherscan-api-key <etherscan api key> \
--verify --broadcast
# Define parameters
OWNER=<owner address>
BURNER_ROUTER_FACTORY=0x99F2B89fB3C363fBafD8d826E5AA77b28bAB70a0
COLLATERAL=0xC329400492c6ff2438472D4651Ad17389fCb843a # wstETH
DELAY=$((2*24*60*60)) # 2 days
GLOBAL_RECEIVER=<treasury address>
# For the array parameters, format exactly as shown
NETWORK_RECEIVERS="[(<network address>,<receiver address>)]"
OPERATOR_NETWORK_RECEIVERS="[(<network address>,<operator address>,<receiver address>)]"
# Execute the script
forge script script/operator/01_BurnerRouterSetup.s.sol:BurnerRouterSetup \
--sig "run(address,address,address,uint48,address,(address,address)[],(address,address,address)[])" \
$OWNER $BURNER_ROUTER_FACTORY $COLLATERAL $DELAY $GLOBAL_RECEIVER \
"$NETWORK_RECEIVERS" "$OPERATOR_NETWORK_RECEIVERS" \
--rpc-url https://ethereum-rpc.publicnode.com \
--chain mainnet \
--private-key <your-private-key> \
--etherscan-api-key <etherscan api key> \
--verify --broadcast
Deploy Vault Delegator + Slasher
A Vault is the core Symbiotic primitive—an ERC-20 pool that can be restaked across networks and operators.
We use NetworkRestakeDelegator (type 0) for Ditto.
The Veto Slasher lets a resolver veto or approve each slash event.
For the official Symbiotic approach, see: Symbiotic Core Modules Deployment Guide
# Define parameters
VAULT_CONFIGURATOR=0xD2191FE92987171691d552C219b8caEf186eb9cA
OWNER=<owner address>
COLLATERAL=0xB82381A3fBD3FaFA77B3a7bE693342618240067b # wstETH
BURNER=<burner router address from previous step>
EPOCH_DURATION=$((7*24*60*60)) # 7 days
WHITELISTED_DEPOSITORS="[]" # Empty array or specific addresses
DEPOSIT_LIMIT=0 # No limit, or set a specific limit
DELEGATOR_INDEX=0 # 0=NetworkRestakeDelegator, 1=FullRestakeDelegator, 2=OperatorSpecificDelegator, 3=OperatorNetworkSpecificDelegator
HOOK=0x0000000000000000000000000000000000000000 # Contract with onSlash() or zero address
NETWORK=0x683Ed12e213a6D78C338D4e18440C42bb936085A # Network address
WITH_SLASHER=true # Enable slasher
SLASHER_INDEX=1 # 0=Slasher, 1=VetoSlasher
VETO_DURATION=$((24*60*60)) # 1 day
# Execute the script
forge script script/operator/02_CoreSetup.s.sol:CoreSetup \
--sig "run(address,address,address,address,uint48,address[],uint256,uint64,address,address,bool,uint64,uint48)" \
$VAULT_CONFIGURATOR $OWNER $COLLATERAL $BURNER $EPOCH_DURATION \
"$WHITELISTED_DEPOSITORS" $DEPOSIT_LIMIT $DELEGATOR_INDEX \
$HOOK $NETWORK $WITH_SLASHER $SLASHER_INDEX $VETO_DURATION \
--rpc-url https://ethereum-sepolia-rpc.publicnode.com \
--chain sepolia \
--private-key <your-private-key> \
--etherscan-api-key <etherscan api key> \
--verify --broadcast
# Define parameters
VAULT_CONFIGURATOR=0xD2191FE92987171691d552C219b8caEf186eb9cA
OWNER=<owner address>
COLLATERAL=0xB82381A3fBD3FaFA77B3a7bE693342618240067b # wstETH
BURNER=<burner router address from previous step>
EPOCH_DURATION=$((7*24*60*60)) # 7 days
WHITELISTED_DEPOSITORS="[]" # Empty array or specific addresses
DEPOSIT_LIMIT=0 # No limit, or set a specific limit
DELEGATOR_INDEX=0 # 0=NetworkRestakeDelegator, 1=FullRestakeDelegator, 2=OperatorSpecificDelegator, 3=OperatorNetworkSpecificDelegator
HOOK=0x0000000000000000000000000000000000000000 # Contract with onSlash() or zero address
NETWORK=0x683Ed12e213a6D78C338D4e18440C42bb936085A # Network address
WITH_SLASHER=true # Enable slasher
SLASHER_INDEX=1 # 0=Slasher, 1=VetoSlasher
VETO_DURATION=$((24*60*60)) # 1 day
# Execute the script
forge script script/operator/02_CoreSetup.s.sol:CoreSetup \
--sig "run(address,address,address,address,uint48,address[],uint256,uint64,address,address,bool,uint64,uint48)" \
$VAULT_CONFIGURATOR $OWNER $COLLATERAL $BURNER $EPOCH_DURATION \
"$WHITELISTED_DEPOSITORS" $DEPOSIT_LIMIT $DELEGATOR_INDEX \
$HOOK $NETWORK $WITH_SLASHER $SLASHER_INDEX $VETO_DURATION \
--rpc-url https://ethereum-sepolia-rpc.publicnode.com \
--chain sepolia \
--private-key <your-private-key> \
--etherscan-api-key <etherscan api key> \
--verify --broadcast
# Define parameters
VAULT_CONFIGURATOR=0x29300b1d3150B4E2b12fE80BE72f365E200441EC
OWNER=<owner address>
COLLATERAL=0xC329400492c6ff2438472D4651Ad17389fCb843a # wstETH
BURNER=<burner router address from previous step>
EPOCH_DURATION=$((7*24*60*60)) # 7 days
WHITELISTED_DEPOSITORS="[]" # Empty array or specific addresses
DEPOSIT_LIMIT=0 # No limit, or set a specific limit
DELEGATOR_INDEX=0 # 0=NetworkRestakeDelegator, 1=FullRestakeDelegator, 2=OperatorSpecificDelegator, 3=OperatorNetworkSpecificDelegator
HOOK=0x0000000000000000000000000000000000000000 # Contract with onSlash() or zero address
NETWORK=0x8560C667Ae72F28D09465B342A480daB28821f6b # Network address
WITH_SLASHER=true # Enable slasher
SLASHER_INDEX=1 # 0=Slasher, 1=VetoSlasher
VETO_DURATION=$((24*60*60)) # 1 day
# Execute the script
forge script script/operator/02_CoreSetup.s.sol:CoreSetup \
--sig "run(address,address,address,address,uint48,address[],uint256,uint64,address,address,bool,uint64,uint48)" \
$VAULT_CONFIGURATOR $OWNER $COLLATERAL $BURNER $EPOCH_DURATION \
"$WHITELISTED_DEPOSITORS" $DEPOSIT_LIMIT $DELEGATOR_INDEX \
$HOOK $NETWORK $WITH_SLASHER $SLASHER_INDEX $VETO_DURATION \
--rpc-url https://ethereum-rpc.publicnode.com \
--chain mainnet \
--private-key <your-private-key> \
--etherscan-api-key <etherscan api key> \
--verify --broadcast
Deploy Default Staker Rewards
DefaultStakerRewards
harvests rewards earned by the Vault and redistributes
them to depositors, taking an optional admin fee.
For the official Symbiotic approach, see: Symbiotic Staker Rewards Deployment Guide
# Define parameters
VAULT=<vault address from previous step>
ADMIN_FEE=1000 # 10% (basis points, 10000 = 100%)
DEFAULT_ADMIN_ROLE_HOLDER=<admin address>
ADMIN_FEE_CLAIM_ROLE_HOLDER=<admin address>
ADMIN_FEE_SET_ROLE_HOLDER=<admin address>
DEFAULT_STAKER_REWARDS_FACTORY=0xE6381EDA7444672da17Cd859e442aFFcE7e170F0
# Execute the script
forge script script/operator/03_DefaultStakerRewardsSetup.s.sol:DefaultStakerRewardsSetup \
--sig "run(address,uint256,address,address,address,address)" \
$VAULT $ADMIN_FEE $DEFAULT_ADMIN_ROLE_HOLDER $ADMIN_FEE_CLAIM_ROLE_HOLDER \
$ADMIN_FEE_SET_ROLE_HOLDER $DEFAULT_STAKER_REWARDS_FACTORY \
--rpc-url https://ethereum-sepolia-rpc.publicnode.com \
--chain sepolia \
--private-key <your-private-key> \
--etherscan-api-key <etherscan api key> \
--broadcast
# Define parameters
VAULT=<vault address from previous step>
ADMIN_FEE=1000 # 10% (basis points, 10000 = 100%)
DEFAULT_ADMIN_ROLE_HOLDER=<admin address>
ADMIN_FEE_CLAIM_ROLE_HOLDER=<admin address>
ADMIN_FEE_SET_ROLE_HOLDER=<admin address>
DEFAULT_STAKER_REWARDS_FACTORY=0xE6381EDA7444672da17Cd859e442aFFcE7e170F0
# Execute the script
forge script script/operator/03_DefaultStakerRewardsSetup.s.sol:DefaultStakerRewardsSetup \
--sig "run(address,uint256,address,address,address,address)" \
$VAULT $ADMIN_FEE $DEFAULT_ADMIN_ROLE_HOLDER $ADMIN_FEE_CLAIM_ROLE_HOLDER \
$ADMIN_FEE_SET_ROLE_HOLDER $DEFAULT_STAKER_REWARDS_FACTORY \
--rpc-url https://ethereum-sepolia-rpc.publicnode.com \
--chain sepolia \
--private-key <your-private-key> \
--etherscan-api-key <etherscan api key> \
--broadcast
# Define parameters
VAULT=<vault address from previous step>
ADMIN_FEE=1000 # 10% (basis points, 10000 = 100%)
DEFAULT_ADMIN_ROLE_HOLDER=<admin address>
ADMIN_FEE_CLAIM_ROLE_HOLDER=<admin address>
ADMIN_FEE_SET_ROLE_HOLDER=<admin address>
DEFAULT_STAKER_REWARDS_FACTORY=0xFEB871581C2ab2e1EEe6f7dDC7e6246cFa087A23
# Execute the script
forge script script/operator/03_DefaultStakerRewardsSetup.s.sol:DefaultStakerRewardsSetup \
--sig "run(address,uint256,address,address,address,address)" \
$VAULT $ADMIN_FEE $DEFAULT_ADMIN_ROLE_HOLDER $ADMIN_FEE_CLAIM_ROLE_HOLDER \
$ADMIN_FEE_SET_ROLE_HOLDER $DEFAULT_STAKER_REWARDS_FACTORY \
--rpc-url https://ethereum-rpc.publicnode.com \
--chain mainnet \
--private-key <your-private-key> \
--etherscan-api-key <etherscan api key> \
--broadcast
Deposit Collateral
After the vault, burner router, delegator, and slasher are deployed in the previous step, the vault must obtain deposits of the appropriate ERC20 collateral type. These deposits can come from a variety of sources depending on the vault.
For the official Symbiotic approach, refer to the Symbiotic CLI Guide
Before depositing, ensure you have approved the vault to spend your tokens:
# Approve tokens using cast (replace with your addresses)
cast send <COLLATERAL_TOKEN> "approve(address,uint256)" <VAULT_ADDRESS> <AMOUNT> \
--rpc-url <your-rpc-url> \
--private-key <your-private-key>
Vault Actions
Finally, the vault curator address must make some calls to allocate collateral to the Ditto network and operators.
For Ditto, the subnetwork ID is always 0. The subnetwork bytes32 value is computed by concatenating the network address with the subnetwork ID.
For Mainnet, the subnetwork bytes32 value is:
0x8560C667Ae72F28D09465B342A480daB28821f6b000000000000000000000000
For Sepolia:
0x683Ed12e213a6D78C338D4e18440C42bb936085A000000000000000000000000
Set Network Limit
First, call setNetworkLimit(bytes32 subnetwork, uint256 amount)
on the delegator module of the vault. This sets the total amount of collateral the vault would like to restake to the Ditto network.
# Using cast (replace with your addresses and values)
DELEGATOR=<delegator address> # The delegator module of your vault
SUBNETWORK=0x8560C667Ae72F28D09465B342A480daB28821f6b000000000000000000000000 # For mainnet
LIMIT=<amount> # Amount in wei (e.g., 1000000000000000000000 for 1000 tokens)
cast send $DELEGATOR "setNetworkLimit(bytes32,uint256)" $SUBNETWORK $LIMIT \
--rpc-url https://ethereum-rpc.publicnode.com \
--private-key <your-private-key>
Set Operator Network Shares (for NetworkRestakeDelegator)
NetworkRestakeDelegator (type 0)
You must also call setOperatorNetworkShares(subnetwork, operator, shares)
to allocate stake to specific operators.
OperatorSpecificDelegator (type 2)
This step is not required as 100% of shares are automatically allocated to the single operator.
# Using cast (replace with your addresses and values)
DELEGATOR=<delegator address> # The delegator module of your vault
SUBNETWORK=0x8560C667Ae72F28D09465B342A480daB28821f6b000000000000000000000000 # For mainnet
OPERATOR=<operator address> # Your operator address
SHARES=<shares> # Amount in basis points (e.g., 10000 for 100%)
cast send $DELEGATOR "setOperatorNetworkShares(bytes32,address,uint256)" $SUBNETWORK $OPERATOR $SHARES \
--rpc-url https://ethereum-rpc.publicnode.com \
--private-key <your-private-key>
Here is an example of how the vault curator would complete this step using a Foundry script:
// SaveToFile: VaultActions.s.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import {Script} from "forge-std/Script.sol";
import {console2} from "forge-std/console2.sol";
import {IOperatorSpecificDelegator} from "symbiotic-core/src/interfaces/delegator/IOperatorSpecificDelegator.sol";
contract VaultActions is Script {
function run() external {
vm.startBroadcast();
// Replace with your delegator address
IOperatorSpecificDelegator delegator = IOperatorSpecificDelegator(0x75b131De299A5D343b9408081DD6A9891b8c);
// Set network limit (1,000,000 tokens with 18 decimals)
delegator.setNetworkLimit(0x8560C667Ae72F28D09465B342A480daB28821f6b000000000000000000000000, 1000000 ether);
// Check stake allocation for the operator
uint256 stake = delegator.stake(0x8560C667Ae72F28D09465B342A480daB28821f6b000000000000000000000000, msg.sender);
console2.log("Stake toward operator:", stake);
vm.stopBroadcast();
}
}
To run this script:
# Create the script file and paste the content above
# Execute the script
forge script VaultActions.s.sol:VaultActions \
--rpc-url https://ethereum-rpc.publicnode.com \
--private-key <your-private-key> \
--etherscan-api-key <etherscan api key> \
--broadcast
Troubleshooting
If you encounter any issues during the setup process, check our common troubleshooting solutions:
For further assistance, contact Ditto support.