Skip to content
LogoLogo

Deploy Vault

Symbiotic is a highly modular protocol with many separate parts and more coming in the future. This guide describes possible vault deployment configurations.

General-purpose deployment

To deploy a ready-to-go vault, first determine how it will be used:

  1. As a very specific configuration inside some complex system of contracts that you design and create
  2. As a very pure configuration with most parameters non-updatable that is difficult to manage in practice
  3. As some common configuration that may cover most of the use cases

We choose the 3rd option. Follow these steps to get a production-ready configuration:

  1. Deploy a Burner Router (a specific implementation of the Burner module)

  2. Deploy core modules such as Vault, Delegator, Slasher using the deployed Burner Router address

  3. Deploy staker rewards with the use of the Vault’s address got during the previous step

1. Burner Router

A burner is a contract that receives slashed collateral tokens. Symbiotic doesn't specify how funds are processed after slashing. Possible approaches include:

  • Fully burn the funds (unwrap the underlying assets if needed)
  • Redistribute to good operators
  • Compensate the victims (in case of using the stake as a security deposit)

From our side, we provide:

  • DefaultBurners - pure burner contracts for several ETH LSTs
  • BurnerRouter - a router contract that allows redirection of the slashed funds to different addresses depending on the slashing networks and the slashed operators

Our pure burners have one advantage and disadvantage: they are fully immutable, removing trust assumptions, but if the underlying LST's contracts upgrade, their flow may break. We recommend using BurnerRouter, which includes receiver update functionality, paired with a DefaultBurner as a global receiver.

burner

Burner Router deployment
  1. Clone the burners repository by running the following command:
bash
git clone --recurse-submodules https://github.com/symbioticfi/burners.git
  1. Navigate into the cloned repository folder:
bash
cd burners
  1. Deploy a burner router contract using a simple script:
bash
OWNER=0xe8616DEcea16b5216e805B0b8caf7784de7570E7                 # address of the router’s owner
COLLATERAL=0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0            # address of the collateral - wstETH (MUST be the same as for the Vault to connect)
DELAY=1814400                                                    # duration of the receivers’ update delay (= 21 days)
GLOBAL_RECEIVER=0xdCaC890b14121FD5D925E2589017Be68C2B5B324       # address of the pure burner corresponding to the collateral - wstETH_Burner (some collaterals are covered by us; see Deployments page)
NETWORK_RECEIVERS=[]                                             # array with elements like "\(network_address,receiver_address\)" meaning network-specific receivers
OPERATOR_NETWORK_RECEIVERS=[]                                    # array with elements like "\(network_address,operator_address,receiver_address\)" meaning operator-network-specific receivers
 
forge script script/deploy/BurnerRouter.s.sol:BurnerRouterScript \
  $OWNER \
  $COLLATERAL \
  $DELAY \
  $GLOBAL_RECEIVER \
  $NETWORK_RECEIVERS \
  $OPERATOR_NETWORK_RECEIVERS \
--sig "run(address,address,uint48,address,(address,address)[],(address,address,address)[])" \
--rpc-url=https://ethereum-rpc.publicnode.com \
--chain mainnet \
--broadcast
Burner Router deployment - technical (Solidity)

The following instruction assumes you have an already initialized Foundry repository (read more here).

  1. Install the burners repository by running the following command:
bash
forge install symbioticfi/burners
  1. Update (or create if not yet) a remappings.txt file inside your repository accordingly:
remappings.txt
...
@symbioticfi/burners/=lib/burners/
  1. Create a burner router contract using a BurnerRouterFactory contract:
DeployBurnerRouter.s.sol
import {IBurnerRouterFactory} from "@symbioticfi/burners/src/interfaces/router/IBurnerRouterFactory.sol";
import {IBurnerRouter} from "@symbioticfi/burners/src/interfaces/router/IBurnerRouter.sol";
 
// ...
 
   address BURNER_ROUTER_FACTORY = 0x99F2B89fB3C363fBafD8d826E5AA77b28bAB70a0;          // address of the BurnerRouterFactory (see Deployments page)
 
// ...
 
      address burnerRouter = IBurnerRouterFactory(BURNER_ROUTER_FACTORY).create(
         IBurnerRouter.InitParams({
               owner: 0xe8616DEcea16b5216e805B0b8caf7784de7570E7,                       // address of the router’s owner
               collateral: 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0,                  // address of the collateral - wstETH (MUST be the same as for the Vault to connect)
               delay: 1814400,                                                          // duration of the receivers’ update delay (= 21 days)
               globalReceiver: 0xdCaC890b14121FD5D925E2589017Be68C2B5B324,              // address of the pure burner corresponding to the collateral - wstETH_Burner (some collaterals are covered by us; see Deployments page)
               networkReceivers: new IBurnerRouter.NetworkReceiver[](0),                // array with IBurnerRouter.NetworkReceiver elements meaning network-specific receivers
               operatorNetworkReceivers: new IBurnerRouter.OperatorNetworkReceiver[](0) // array with IBurnerRouter.OperatorNetworkReceiver elements meaning network-specific receivers
         })
      );
 
// ...

2. Core modules

Vault

Vault is a contract that:

  • storages the collateral
  • performs accounting in the sense of deposits and withdrawals
  • processes slashing events by transferring the collateral to the burner
  • allows deposit whitelisting and deposit limiting

We provide two types of vaults:

  1. Vault (version 1) - a standard version responsible for all the functions mentioned above
  2. Tokenized Vault (version 2) - an extended version that represents stake shares as ERC-20 tokens

We don't need tokenization of the stake as LRTs provide it. Therefore, we'll use the common Vault.

vault

Delegator

A delegator is a contract that allows vault curators to allocate stake to networks and operators.

Currently, we have four types:

  1. NetworkRestakeDelegator (type 0) - accounts for allocations in absolute numbers for networks and in shares for operator-network pairs, enabling staking and restaking across networks (depending on delegated amounts)
  2. FullRestakeDelegator (type 1) - accounts for allocations in absolute numbers for both networks and operator-network pairs, enabling everything NetworkRestakeDelegator allows plus restaking across operators within networks
  3. OperatorSpecificDelegator (type 2) - a simplified version of NetworkRestakeDelegator where only one specific operator has allocations
  4. OperatorNetworkSpecificDelegator (type 3) - the simplest version where only one specific operator at one specific network has an allocation

FullRestakeDelegator covers all delegation use cases but can create highly risky configurations that need proper handling. OperatorSpecificDelegator limits use cases for LRTs with multiple operators, and OperatorNetworkSpecificDelegator has more limitations by design. We'll choose NetworkRestakeDelegator for our needs.

delegator

Slasher

A Slasher is a contract responsible for proper penalty execution, preserving networks' rights to slash captured stake and stakers' rights not to be slashed more than deserved.

There are two types of slasher:

  1. Slasher (type 0) - a common slasher that receives slashing requests and instantly executes them
  2. VetoSlasher (type 1) - allows vetoing received slashing requests using resolvers

For VetoSlasher, networks may propose resolvers that can veto slashing requests. Networks can also choose not to set a resolver, enabling an instant slashing mechanic similar to Slasher's. If a vault curator isn't ready to provide stake without a resolver, the curator may simply not allocate any stake to such networks. Since VetoSlasher extends Slasher, we'll choose it.

slasher

Core modules deployment
  1. Clone the core contracts repository by running the following command:

    bash
    git clone --recurse-submodules https://github.com/symbioticfi/core.git
  2. Navigate into the cloned repository folder:

    bash
    cd core
  3. Deploy core modules contracts using a simple script: Open DeployVault.s.sol and configure these settings:

    bash
      // Address of the owner of the vault who can migrate the vault to new versions whitelisted by Symbiotic
      address OWNER = 0x0000000000000000000000000000000000000000;
      // Address of the collateral token
      address COLLATERAL = 0x0000000000000000000000000000000000000000;
      // Vault's burner to send slashed funds to (e.g., 0xdEaD or some unwrapper contract; not used in case of no slasher)
      address BURNER = 0x000000000000000000000000000000000000dEaD;
      // Duration of the vault epoch (the withdrawal delay for staker varies from EPOCH_DURATION to 2 * EPOCH_DURATION depending on when the withdrawal is requested)
      uint48 EPOCH_DURATION = 7 days;
      // Type of the delegator:
      //  0. NetworkRestakeDelegator (allows restaking across multiple networks and having multiple operators per network)
      //  1. FullRestakeDelegator (do not use without knowing what you are doing)
      //  2. OperatorSpecificDelegator (allows restaking across multiple networks with only a single operator)
      //  3. OperatorNetworkSpecificDelegator (allocates the stake to a specific operator and network)
      uint64 DELEGATOR_INDEX = 0;
      // Setting depending on the delegator type:
      // 0. NetworkLimitSetRoleHolders (adjust allocations for networks)
      // 1. NetworkLimitSetRoleHolders (adjust allocations for networks)
      // 2. NetworkLimitSetRoleHolders (adjust allocations for networks)
      // 3. network (the only network that will receive the stake; should be an array with a single element)
      address[] NETWORK_ALLOCATION_SETTERS_OR_NETWORK = [0x0000000000000000000000000000000000000000];
      // Setting depending on the delegator type:
      // 0. OperatorNetworkSharesSetRoleHolders (adjust allocations for operators inside networks; in shares, resulting percentage is operatorShares / totalOperatorShares)
      // 1. OperatorNetworkLimitSetRoleHolders (adjust allocations for operators inside networks; in shares, resulting percentage is operatorShares / totalOperatorShares)
      // 2. operator (the only operator that will receive the stake; should be an array with a single element)
      // 3. operator (the only operator that will receive the stake; should be an array with a single element)
      address[] OPERATOR_ALLOCATION_SETTERS_OR_OPERATOR = [0x0000000000000000000000000000000000000000];
      // Whether to deploy a slasher
      bool WITH_SLASHER = true;
      // Type of the slasher:
      //  0. Slasher (allows instant slashing)
      //  1. VetoSlasher (allows having a veto period if the resolver is set)
      uint64 SLASHER_INDEX = 1;
      // Duration of a veto period (should be less than EPOCH_DURATION)
      uint48 VETO_DURATION = 1 days;
     
      // Optional
     
      // Deposit limit (maximum amount of the active stake allowed in the vault)
      uint256 DEPOSIT_LIMIT = 0;
      // Addresses of the whitelisted depositors
      address[] WHITELISTED_DEPOSITORS = new address[](0);
      // Address of the hook contract which, e.g., can automatically adjust the allocations on slashing events (not used in case of no slasher)
      address HOOK = 0x0000000000000000000000000000000000000000;
      // Delay in epochs for a network to update a resolver
      uint48 RESOLVER_SET_EPOCHS_DELAY = 3;

Edit the configuration fields, then run:

bash
forge script script/DeployVault.s.sol:DeployVaultScript \
   --rpc-url=https://ethereum-rpc.publicnode.com \
   --chain mainnet \
   --broadcast
Core modules deployment - technical (Solidity)

The following instruction assumes you have an already initialized Foundry repository (read more here).

  1. Install the core contracts repository by running the following command:
bash
forge install symbioticfi/core
  1. Update (or create if not yet) a remappings.txt file inside your repository accordingly:
remappings.txt
...
@symbioticfi/core/=lib/core/
  1. Create core modules using a VaultConfigurator contract:
DeployVault.s.sol
import {IVaultConfigurator} from "@symbioticfi/core/src/interfaces/IVaultConfigurator.sol";
import {IVault} from "@symbioticfi/core/src/interfaces/vault/IVault.sol";
import {IBaseDelegator} from "@symbioticfi/core/src/interfaces/delegator/IBaseDelegator.sol";
import {INetworkRestakeDelegator} from "@symbioticfi/core/src/interfaces/delegator/INetworkRestakeDelegator.sol";
import {IBaseSlasher} from "@symbioticfi/core/src/interfaces/slasher/IBaseSlasher.sol";
import {IVetoSlasher} from "@symbioticfi/core/src/interfaces/slasher/IVetoSlasher.sol";
 
// ...
 
   address VAULT_CONFIGURATOR = 0x29300b1d3150B4E2b12fE80BE72f365E200441EC;                  // address of the VaultConfigurator (see Deployments page)
 
// ...
 
      address[] memory networkLimitSetRoleHolders = new address[](1);
      networkLimitSetRoleHolders[0] = 0xe8616DEcea16b5216e805B0b8caf7784de7570E7;
      address[] memory operatorNetworkSharesSetRoleHolders = new address[](1);
      operatorNetworkSharesSetRoleHolders[0] = 0xe8616DEcea16b5216e805B0b8caf7784de7570E7;
      (address vault, address networkRestakeDelegator, address vetoSlasher) = IVaultConfigurator(VAULT_CONFIGURATOR).create(
         IVaultConfigurator.InitParams({
               version: 1,                                                                   // Vault’s version (= common one)
               owner: 0xe8616DEcea16b5216e805B0b8caf7784de7570E7,                            // address of the Vault’s owner (can migrate the Vault to new versions in the future)
               vaultParams: abi.encode(IVault.InitParams({
                  collateral: 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0,                    // address of the collateral - wstETH
                  burner: <BURNER_ROUTER>,                                                   // address of the deployed burner router
                  epochDuration: 604800,                                                     // duration of the Vault epoch in seconds (= 7 days)
                  depositWhitelist: false,                                                   // if enable deposit whitelisting
                  isDepositLimit: false,                                                     // if enable deposit limit
                  depositLimit: 0,                                                           // deposit limit
                  defaultAdminRoleHolder: 0xe8616DEcea16b5216e805B0b8caf7784de7570E7,        // address of the Vault’s admin (can manage all roles)
                  depositWhitelistSetRoleHolder: 0xe8616DEcea16b5216e805B0b8caf7784de7570E7, // address of the enabler/disabler of the deposit whitelisting
                  depositorWhitelistRoleHolder: 0xe8616DEcea16b5216e805B0b8caf7784de7570E7,  // address of the depositors whitelister
                  isDepositLimitSetRoleHolder: 0xe8616DEcea16b5216e805B0b8caf7784de7570E7,   // address of the enabler/disabler of the deposit limit
                  depositLimitSetRoleHolder: 0xe8616DEcea16b5216e805B0b8caf7784de7570E7      // address of the deposit limit setter
               })),
               delegatorIndex: 0,                                                            // Delegator’s type (= NetworkRestakeDelegator)
               delegatorParams: abi.encode(INetworkRestakeDelegator.InitParams({
                  baseParams: IBaseDelegator.BaseParams({
                     defaultAdminRoleHolder: 0xe8616DEcea16b5216e805B0b8caf7784de7570E7,     // address of the Delegator’s admin (can manage all roles)
                     hook: 0x0000000000000000000000000000000000000000,                       // address of the hook (if not zero, receives onSlash() call on each slashing)
                     hookSetRoleHolder: 0xe8616DEcea16b5216e805B0b8caf7784de7570E7           // address of the hook setter
                  }),
                  networkLimitSetRoleHolders: networkLimitSetRoleHolders,                    // array of addresses of the network limit setters
                  operatorNetworkSharesSetRoleHolders: operatorNetworkSharesSetRoleHolders   // array of addresses of the operator-network shares setters
               })),
               withSlasher: true,                                                            // if enable Slasher module
               slasherIndex: 1,                                                              // Slasher’s type (= VetoSlasher)
               slasherParams: abi.encode(IVetoSlasher.InitParams({
                  baseParams: IBaseSlasher.BaseParams({
                     isBurnerHook: true                                                      // if enable the `burner` to receive onSlash() call after each slashing (is needed for the burner router workflow)
                  }),
                  vetoDuration: 86400,                                                       // veto duration (= 1 day)
                  resolverSetEpochsDelay: 3                                                  // number of Vault epochs needed for the resolver to be changed
               }))
         })
      );
 
// ...

3. Staker rewards

Rewards logic is not enshrined in the core contract, and we allow anyone to create their own implementations if needed. However, it requires resources like time, knowledge, and money. Therefore, we provide a default implementation of the staker rewards named DefaultStakerRewards. Its main goals are:

  • allow networks to distribute rewards for the stakers of the certain Vault
  • allow stakers to claim these rewards
  • allow Vault curators to receive fees from the distributed rewards

This is the only staker rewards implementation we provide, so let's add it to our deployment configuration.

staker_rewards

Staker rewards deployment
  1. Clone the rewards contracts repository by running the following command:

    bash
    git clone --recurse-submodules https://github.com/symbioticfi/rewards.git
  2. Navigate into the cloned repository folder:

    bash
    cd rewards
  3. Deploy a staker rewards contract using a simple script:

bash
VAULT=<VAULT>                                                             # address of the deployed Vault
ADMIN_FEE=1000                                                            # admin fee percent to get from all the rewards distributions (10% = 1_000 | 100% = 10_000)
ADMIN=0xe8616DEcea16b5216e805B0b8caf7784de7570E7                          # address of the main admin (can manage all roles)
ADMIN_FEE_CLAIMER=0xe8616DEcea16b5216e805B0b8caf7784de7570E7              # address of the admin fee claimer
ADMIN_FEE_SETTER=0xe8616DEcea16b5216e805B0b8caf7784de7570E7               # address of the admin fee setter
 
forge script script/deploy/DefaultStakerRewards.s.sol:DefaultStakerRewardsScript \
   $VAULT \
   $ADMIN_FEE \
   $ADMIN \
   $ADMIN_FEE_CLAIMER \
   $ADMIN_FEE_SETTER \
--sig "run(address,uint256,address,address,address)" \
--rpc-url=https://ethereum-rpc.publicnode.com \
--chain mainnet \
--broadcast
Staker rewards deployment - technical (Solidity)

The following instruction assumes you have an already initialized Foundry repository (read more here).

  1. Install the rewards contracts repository by running the following command:
bash
forge install symbioticfi/rewards
  1. Update (or create if not yet) a remappings.txt file inside your repository accordingly:
remappings.txt
...
@symbioticfi/rewards/=lib/rewards/
  1. Create a staker rewards contract using a DefaultStakerRewardsFactory contract:
DefaultStakerRewards.s.sol
import {IDefaultStakerRewardsFactory} from "@symbioticfi/rewards/src/interfaces/defaultStakerRewards/IDefaultStakerRewardsFactory.sol";
import {IDefaultStakerRewards} from "@symbioticfi/rewards/src/interfaces/defaultStakerRewards/IDefaultStakerRewards.sol";
 
// ...
 
   address DEFAULT_STAKER_REWARDS_FACTORY = 0x290CAB97a312164Ccf095d75D6175dF1C4A0a25F; // address of the DefaultStakerRewardsFactory (see Deployments page)
 
// ...
 
      address defaultStakerRewards = IDefaultStakerRewardsFactory(DEFAULT_STAKER_REWARDS_FACTORY).create(IDefaultStakerRewards.InitParams({
         vault: <VAULT>,                                                                // address of the deployed Vault
         adminFee: 1000,                                                                // admin fee percent to get from all the rewards distributions (10% = 1_000 | 100% = 10_000)
         defaultAdminRoleHolder: 0xe8616DEcea16b5216e805B0b8caf7784de7570E7,            // address of the main admin (can manage all roles)
         adminFeeClaimRoleHolder: 0xe8616DEcea16b5216e805B0b8caf7784de7570E7,           // address of the admin fee claimer
         adminFeeSetRoleHolder: 0xe8616DEcea16b5216e805B0b8caf7784de7570E7              // address of the admin fee setter
      }));
 
// ...

Advanced

Network-specific burners

The further text describes extended configurations that may bring risk reductions, wider support of the networks, and more capital efficiency for users.

Such a case is possible:

  • A vault typically wants slashed funds to be burnt.
  • Alternatively, a highly reputable network wants its slashings redistributed to users who could suffer losses due to operators' malicious activity.
  • The vault decides to integrate such a network to increase its capital efficiency.

Currently, our configuration doesn't contain such logic, but it already supports it. As mentioned earlier, BurnerRouter allows splitting slashed funds across any number of receivers depending on the slashing networks and slashed operators. Given the network address that needs this functionality and the burner address to send funds to, you can either configure this behavior using an existing burner router or handle it during deployment.

network-burner

Burner Router with preconfigured network-specific burner deployment
  1. Clone the burners repository by running the following command:

    bash
    git clone --recurse-submodules https://github.com/symbioticfi/burners.git
  2. Navigate into the cloned repository folder:

    bash
    cd burners
  3. Deploy a burner router contract with a preconfigured network-specific burner using a simple script:

bash
OWNER=0xe8616DEcea16b5216e805B0b8caf7784de7570E7                 # address of the router’s owner
COLLATERAL=0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0            # address of the collateral - wstETH (MUST be the same as for the Vault to connect)
DELAY=1814400                                                    # duration of the receivers’ update delay (= 21 days)
GLOBAL_RECEIVER=0xdCaC890b14121FD5D925E2589017Be68C2B5B324       # address of the pure burner corresponding to the collateral - wstETH_Burner (some collaterals are covered by us; see Deployments page)
NETWORK_RECEIVERS=[\(<NETWORK_ADDRESS>,<RECEIVER_ADDRESS>\)]                                             # array with elements like "\(network_address,receiver_address\)" meaning network-specific receivers
OPERATOR_NETWORK_RECEIVERS=[]                                    # array with elements like "\(network_address,operator_address,receiver_address\)" meaning operator-network-specific receivers
 
forge script script/deploy/BurnerRouter.s.sol:BurnerRouterScript \
   $OWNER \
   $COLLATERAL \
   $DELAY \
   $GLOBAL_RECEIVER \
   $NETWORK_RECEIVERS \
   $OPERATOR_NETWORK_RECEIVERS \
--sig "run(address,address,uint48,address,(address,address)[],(address,address,address)[])" \
--rpc-url=https://ethereum-rpc.publicnode.com \
--chain mainnet \
--broadcast
Burner Router with preconfigured network-specific burner deployment - technical (Solidity)

The following instruction assumes you have an already initialized Foundry repository (read more here).

  1. Install the burners repository by running the following command:

    bash
    forge install symbioticfi/burners
  2. Update (or create if not yet) a remappings.txt file inside your repository accordingly:

    remappings.txt
    ...
    @symbioticfi/burners/=lib/burners/
  3. Create a burner router contract using a BurnerRouterFactory contract:

    DeployBurnerRouter.s.sol
    import {IBurnerRouterFactory} from "@symbioticfi/burners/src/interfaces/router/IBurnerRouterFactory.sol";
    import {IBurnerRouter} from "@symbioticfi/burners/src/interfaces/router/IBurnerRouter.sol";
     
    // ...
     
       address BURNER_ROUTER_FACTORY = 0x99F2B89fB3C363fBafD8d826E5AA77b28bAB70a0;          // address of the BurnerRouterFactory (see Deployments page)
     
    // ...
     
          IBurnerRouter.NetworkReceiver[] memory networkReceivers = new IBurnerRouter.NetworkReceiver[](1);
          networkReceivers[0] = IBurnerRouter.NetworkReceiver({
             network: <NETWORK_ADDRESS>,
             receiver: <RECEIVER_ADDRESS>
          });
          address burnerRouter = IBurnerRouterFactory(BURNER_ROUTER_FACTORY).create(
             IBurnerRouter.InitParams({
                   owner: 0xe8616DEcea16b5216e805B0b8caf7784de7570E7,                       // address of the router’s owner
                   collateral: 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0,                  // address of the collateral - wstETH (MUST be the same as for the Vault to connect)
                   delay: 1814400,                                                          // duration of the receivers’ update delay (= 21 days)
                   globalReceiver: 0xdCaC890b14121FD5D925E2589017Be68C2B5B324,              // address of the pure burner corresponding to the collateral - wstETH_Burner (some collaterals are covered by us; see Deployments page)
                   networkReceivers: networkReceivers,                                      // array with IBurnerRouter.NetworkReceiver elements meaning network-specific receivers
                   operatorNetworkReceivers: new IBurnerRouter.OperatorNetworkReceiver[](0) // array with IBurnerRouter.OperatorNetworkReceiver elements meaning network-specific receivers
             })
          );
     
    // ...
Configure Burner Router with network-specific burner after deployment

Generally, all the changes inside the burner router can be performed in 2 steps:

  1. Create a pending request to change some value
  2. Accept the request after waiting for a configured delay

Therefore, to set a new receiver (which can be a network-specific burner or treasury or any address) for a particular network the further steps are needed:

  1. Owner calls setNetworkReceiver(network, receiver)
  2. Wait for delay
  3. Anyone commits the change via acceptNetworkReceiver(network)

To set a new receiver for some exact operator inside a particular network:

  1. Owner calls setOperatorNetworkReceiver(network, operator, receiver)
  2. Wait for delay
  3. Anyone commits the change via acceptOperatorNetworkReceiver(network, operator)

To set a new global receiver that gets funds if no specific receivers for a network-operator pair are configured:

  1. Owner calls setGlobalReceiver(receiver)
  2. Wait for delay
  3. Anyone commits the change via acceptGlobalReceiver(network)

To change a delay:

  1. Owner calls setDelay(newDelay)
  2. Wait for an old delay
  3. Anyone commits the change via acceptDelay()

Hook

Hook is a contract that the Vault curator can set to receive onSlash() call on each slashing event. Symbiotic doesn’t specify any logic behind it. However, the basic idea of the hook is to allow the creation of any delegation adjustment mechanic during the slashing.

We provide pure example implementations of some standard adjustment mechanics for each Delegator type - here.

hook

Core modules deployment with hook
  1. Clone the core contracts repository by running the following command:

    bash
    git clone --recurse-submodules https://github.com/symbioticfi/core.git
  2. Navigate into the cloned repository folder:

    bash
    cd core
  3. Deploy core modules contracts using a simple script: Open DeployVault.s.sol and configure these settings:

    bash
      // Address of the owner of the vault who can migrate the vault to new versions whitelisted by Symbiotic
      address OWNER = 0x0000000000000000000000000000000000000000;
      // Address of the collateral token
      address COLLATERAL = 0x0000000000000000000000000000000000000000;
      // Vault's burner to send slashed funds to (e.g., 0xdEaD or some unwrapper contract; not used in case of no slasher)
      address BURNER = 0x000000000000000000000000000000000000dEaD;
      // Duration of the vault epoch (the withdrawal delay for staker varies from EPOCH_DURATION to 2 * EPOCH_DURATION depending on when the withdrawal is requested)
      uint48 EPOCH_DURATION = 7 days;
      // Type of the delegator:
      //  0. NetworkRestakeDelegator (allows restaking across multiple networks and having multiple operators per network)
      //  1. FullRestakeDelegator (do not use without knowing what you are doing)
      //  2. OperatorSpecificDelegator (allows restaking across multiple networks with only a single operator)
      //  3. OperatorNetworkSpecificDelegator (allocates the stake to a specific operator and network)
      uint64 DELEGATOR_INDEX = 0;
      // Setting depending on the delegator type:
      // 0. NetworkLimitSetRoleHolders (adjust allocations for networks)
      // 1. NetworkLimitSetRoleHolders (adjust allocations for networks)
      // 2. NetworkLimitSetRoleHolders (adjust allocations for networks)
      // 3. network (the only network that will receive the stake; should be an array with a single element)
      address[] NETWORK_ALLOCATION_SETTERS_OR_NETWORK = [0x0000000000000000000000000000000000000000];
      // Setting depending on the delegator type:
      // 0. OperatorNetworkSharesSetRoleHolders (adjust allocations for operators inside networks; in shares, resulting percentage is operatorShares / totalOperatorShares)
      // 1. OperatorNetworkLimitSetRoleHolders (adjust allocations for operators inside networks; in shares, resulting percentage is operatorShares / totalOperatorShares)
      // 2. operator (the only operator that will receive the stake; should be an array with a single element)
      // 3. operator (the only operator that will receive the stake; should be an array with a single element)
      address[] OPERATOR_ALLOCATION_SETTERS_OR_OPERATOR = [0x0000000000000000000000000000000000000000];
      // Whether to deploy a slasher
      bool WITH_SLASHER = true;
      // Type of the slasher:
      //  0. Slasher (allows instant slashing)
      //  1. VetoSlasher (allows having a veto period if the resolver is set)
      uint64 SLASHER_INDEX = 1;
      // Duration of a veto period (should be less than EPOCH_DURATION)
      uint48 VETO_DURATION = 1 days;
     
      // Optional
     
      // Deposit limit (maximum amount of the active stake allowed in the vault)
      uint256 DEPOSIT_LIMIT = 0;
      // Addresses of the whitelisted depositors
      address[] WHITELISTED_DEPOSITORS = new address[](0);
      // Address of the hook contract which, e.g., can automatically adjust the allocations on slashing events (not used in case of no slasher)
      address HOOK = <HOOK>;
      // Delay in epochs for a network to update a resolver
      uint48 RESOLVER_SET_EPOCHS_DELAY = 3;

Edit the configuration fields, then run:

bash
forge script script/DeployVault.s.sol:DeployVaultScript \
   --rpc-url=https://ethereum-rpc.publicnode.com \
   --chain mainnet \
   --broadcast
Core modules deployment with hook - technical (Solidity)

The following instruction assumes you have an already initialized Foundry repository (read more here).

  1. Install the core contracts repository by running the following command:

    bash
    forge install symbioticfi/core
  2. Update (or create if not yet) a remappings.txt file inside your repository accordingly:

remappings.txt
...
@symbioticfi/core/=lib/core/
  1. Create core modules with hook using a VaultConfigurator contract:

    DeployVaultWithHook.s.sol
    import {IVaultConfigurator} from "@symbioticfi/core/src/interfaces/IVaultConfigurator.sol";
    import {IVault} from "@symbioticfi/core/src/interfaces/vault/IVault.sol";
    import {IBaseDelegator} from "@symbioticfi/core/src/interfaces/delegator/IBaseDelegator.sol";
    import {INetworkRestakeDelegator} from "@symbioticfi/core/src/interfaces/delegator/INetworkRestakeDelegator.sol";
    import {IBaseSlasher} from "@symbioticfi/core/src/interfaces/slasher/IBaseSlasher.sol";
    import {IVetoSlasher} from "@symbioticfi/core/src/interfaces/slasher/IVetoSlasher.sol";
     
    // ...
     
       address VAULT_CONFIGURATOR = 0x29300b1d3150B4E2b12fE80BE72f365E200441EC;                  // address of the VaultConfigurator (see Deployments page)
     
    // ...
     
          address hook = <HOOK>;
          address[] memory networkLimitSetRoleHolders = new address[](2);
          networkLimitSetRoleHolders[0] = 0xe8616DEcea16b5216e805B0b8caf7784de7570E7;
          networkLimitSetRoleHolders[1] = hook;
          address[] memory operatorNetworkSharesSetRoleHolders = new address[](2);
          operatorNetworkSharesSetRoleHolders[0] = 0xe8616DEcea16b5216e805B0b8caf7784de7570E7;
          operatorNetworkSharesSetRoleHolders[1] = hook;
     
          (address vault, address networkRestakeDelegator, address vetoSlasher) = IVaultConfigurator(VAULT_CONFIGURATOR).create(
             IVaultConfigurator.InitParams({
                   version: 1,                                                                   // Vault’s version (= common one)
                   owner: 0xe8616DEcea16b5216e805B0b8caf7784de7570E7,                            // address of the Vault’s owner (can migrate the Vault to new versions in the future)
                   vaultParams: abi.encode(IVault.InitParams({
                      collateral: 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0,                    // address of the collateral - wstETH
                      burner: <BURNER_ROUTER>,                                                   // address of the deployed burner router
                      epochDuration: 604800,                                                     // duration of the Vault epoch in seconds (= 7 days)
                      depositWhitelist: false,                                                   // if enable deposit whitelisting
                      isDepositLimit: false,                                                     // if enable deposit limit
                      depositLimit: 0,                                                           // deposit limit
                      defaultAdminRoleHolder: 0xe8616DEcea16b5216e805B0b8caf7784de7570E7,        // address of the Vault’s admin (can manage all roles)
                      depositWhitelistSetRoleHolder: 0xe8616DEcea16b5216e805B0b8caf7784de7570E7, // address of the enabler/disabler of the deposit whitelisting
                      depositorWhitelistRoleHolder: 0xe8616DEcea16b5216e805B0b8caf7784de7570E7,  // address of the depositors whitelister
                      isDepositLimitSetRoleHolder: 0xe8616DEcea16b5216e805B0b8caf7784de7570E7,   // address of the enabler/disabler of the deposit limit
                      depositLimitSetRoleHolder: 0xe8616DEcea16b5216e805B0b8caf7784de7570E7      // address of the deposit limit setter
                   })),
                   delegatorIndex: 0,                                                            // Delegator’s type (= NetworkRestakeDelegator)
                   delegatorParams: abi.encode(INetworkRestakeDelegator.InitParams({
                      baseParams: IBaseDelegator.BaseParams({
                         defaultAdminRoleHolder: 0xe8616DEcea16b5216e805B0b8caf7784de7570E7,     // address of the Delegator’s admin (can manage all roles)
                         hook: hook,                                                             // address of the hook (if not zero, receives onSlash() call on each slashing)
                         hookSetRoleHolder: 0xe8616DEcea16b5216e805B0b8caf7784de7570E7           // address of the hook setter
                      }),
                      networkLimitSetRoleHolders: networkLimitSetRoleHolders,                    // array of addresses of the network limit setters
                      operatorNetworkSharesSetRoleHolders: operatorNetworkSharesSetRoleHolders   // array of addresses of the operator-network shares setters
                   })),
                   withSlasher: true,                                                            // if enable Slasher module
                   slasherIndex: 1,                                                              // Slasher’s type (= VetoSlasher)
                   slasherParams: abi.encode(IVetoSlasher.InitParams({
                      baseParams: IBaseSlasher.BaseParams({
                         isBurnerHook: true                                                      // if enable the `burner` to receive onSlash() call after each slashing (is needed for the burner router workflow)
                      }),
                      vetoDuration: 86400,                                                       // veto duration (= 1 day)
                      resolverSetEpochsDelay: 3                                                  // number of Vault epochs needed for the resolver to be changed
                   }))
             })
          );
     
    // ...
Configure core modules with hook after deployment
  1. We need the address of the Vault’s Delegator contract. It can be obtained via Vault.delegator()
  2. Make sure the caller has a Delegator.HOOK_SET_ROLE() role via the OpenZeppelin’s AccessControl contract
  3. Call Delegator.setHook(hook)

Deposit whitelist

Symbiotic Vaults contain a feature called - deposit whitelist. It allows the restriction of making deposits to the Vault only by the whitelisted depositors. Several parties may need such functionality, e.g.:

  1. LRTs that want to fully isolate their Vaults from the external users to have a strict logic flow depending only on them
  2. Institutional entities that want to reduce the risks maximally and respect the legal side
  3. Some third-party entities that want to integrate the Vaults with some specific logic on top of them

whitelist

Core modules deployment with deposit whitelist
  1. Clone the core contracts repository by running the following command:

    bash
    git clone --recurse-submodules https://github.com/symbioticfi/core.git
  2. Navigate into the cloned repository folder:

    bash
    cd core
  3. Deploy core modules contracts using a simple script: Open DeployVault.s.sol and configure these settings:

    bash
      // Address of the owner of the vault who can migrate the vault to new versions whitelisted by Symbiotic
      address OWNER = 0x0000000000000000000000000000000000000000;
      // Address of the collateral token
      address COLLATERAL = 0x0000000000000000000000000000000000000000;
      // Vault's burner to send slashed funds to (e.g., 0xdEaD or some unwrapper contract; not used in case of no slasher)
      address BURNER = 0x000000000000000000000000000000000000dEaD;
      // Duration of the vault epoch (the withdrawal delay for staker varies from EPOCH_DURATION to 2 * EPOCH_DURATION depending on when the withdrawal is requested)
      uint48 EPOCH_DURATION = 7 days;
      // Type of the delegator:
      //  0. NetworkRestakeDelegator (allows restaking across multiple networks and having multiple operators per network)
      //  1. FullRestakeDelegator (do not use without knowing what you are doing)
      //  2. OperatorSpecificDelegator (allows restaking across multiple networks with only a single operator)
      //  3. OperatorNetworkSpecificDelegator (allocates the stake to a specific operator and network)
      uint64 DELEGATOR_INDEX = 0;
      // Setting depending on the delegator type:
      // 0. NetworkLimitSetRoleHolders (adjust allocations for networks)
      // 1. NetworkLimitSetRoleHolders (adjust allocations for networks)
      // 2. NetworkLimitSetRoleHolders (adjust allocations for networks)
      // 3. network (the only network that will receive the stake; should be an array with a single element)
      address[] NETWORK_ALLOCATION_SETTERS_OR_NETWORK = [0x0000000000000000000000000000000000000000];
      // Setting depending on the delegator type:
      // 0. OperatorNetworkSharesSetRoleHolders (adjust allocations for operators inside networks; in shares, resulting percentage is operatorShares / totalOperatorShares)
      // 1. OperatorNetworkLimitSetRoleHolders (adjust allocations for operators inside networks; in shares, resulting percentage is operatorShares / totalOperatorShares)
      // 2. operator (the only operator that will receive the stake; should be an array with a single element)
      // 3. operator (the only operator that will receive the stake; should be an array with a single element)
      address[] OPERATOR_ALLOCATION_SETTERS_OR_OPERATOR = [0x0000000000000000000000000000000000000000];
      // Whether to deploy a slasher
      bool WITH_SLASHER = true;
      // Type of the slasher:
      //  0. Slasher (allows instant slashing)
      //  1. VetoSlasher (allows having a veto period if the resolver is set)
      uint64 SLASHER_INDEX = 1;
      // Duration of a veto period (should be less than EPOCH_DURATION)
      uint48 VETO_DURATION = 1 days;
     
      // Optional
     
      // Deposit limit (maximum amount of the active stake allowed in the vault)
      uint256 DEPOSIT_LIMIT = 0;
      // Addresses of the whitelisted depositors
      address[] WHITELISTED_DEPOSITORS = <DEPOSITORS_TO_WHITELIST>;
      // Address of the hook contract which, e.g., can automatically adjust the allocations on slashing events (not used in case of no slasher)
      address HOOK = 0x0000000000000000000000000000000000000000;
      // Delay in epochs for a network to update a resolver
      uint48 RESOLVER_SET_EPOCHS_DELAY = 3;

Edit the configuration fields, then run:

bash
forge script script/DeployVault.s.sol:DeployVaultScript \
   --rpc-url=https://ethereum-rpc.publicnode.com \
   --chain mainnet \
   --broadcast
Core modules deployment with deposit whitelist - technical (Solidity)

The following instruction assumes you have an already initialized Foundry repository (read more here).

  1. Install the core contracts repository by running the following command:

    bash
    forge install symbioticfi/core
  2. Update (or create if not yet) a remappings.txt file inside your repository accordingly:

    remappings.txt
    ...
    @symbioticfi/core/=lib/core/
  3. Create core modules with deposit whitelist using a VaultConfigurator contract:

    DeployVaultWithWhitelist.s.sol
    import {Vault} from "@symbioticfi/core/src/contracts/vault/Vault.sol";
    import {IVaultConfigurator} from "@symbioticfi/core/src/interfaces/IVaultConfigurator.sol";
    import {IVault} from "@symbioticfi/core/src/interfaces/vault/IVault.sol";
    import {IBaseDelegator} from "@symbioticfi/core/src/interfaces/delegator/IBaseDelegator.sol";
    import {INetworkRestakeDelegator} from "@symbioticfi/core/src/interfaces/delegator/INetworkRestakeDelegator.sol";
    import {IBaseSlasher} from "@symbioticfi/core/src/interfaces/slasher/IBaseSlasher.sol";
    import {IVetoSlasher} from "@symbioticfi/core/src/interfaces/slasher/IVetoSlasher.sol";
     
    // ...
     
       address VAULT_CONFIGURATOR = 0x29300b1d3150B4E2b12fE80BE72f365E200441EC;                  // address of the VaultConfigurator (see Deployments page)
     
    // ...
     
          address[] memory networkLimitSetRoleHolders = new address[](1);
          networkLimitSetRoleHolders[0] = 0xe8616DEcea16b5216e805B0b8caf7784de7570E7;
          address[] memory operatorNetworkSharesSetRoleHolders = new address[](1);
          operatorNetworkSharesSetRoleHolders[0] = 0xe8616DEcea16b5216e805B0b8caf7784de7570E7;
     
          (address vault, address networkRestakeDelegator, address vetoSlasher) = IVaultConfigurator(VAULT_CONFIGURATOR).create(
             IVaultConfigurator.InitParams({
                   version: 1,                                                                   // Vault’s version (= common one)
                   owner: 0xe8616DEcea16b5216e805B0b8caf7784de7570E7,                            // address of the Vault’s owner (can migrate the Vault to new versions in the future)
                   vaultParams: abi.encode(IVault.InitParams({
                      collateral: 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0,                    // address of the collateral - wstETH
                      burner: <BURNER_ROUTER>,                                                   // address of the deployed burner router
                      epochDuration: 604800,                                                     // duration of the Vault epoch in seconds (= 7 days)
                      depositWhitelist: true,                                                    // if enable deposit whitelisting
                      isDepositLimit: false,                                                     // if enable deposit limit
                      depositLimit: 0,                                                           // deposit limit
                      defaultAdminRoleHolder: address(this),                                     // address of the Vault’s admin (can manage all roles)
                      depositWhitelistSetRoleHolder: 0xe8616DEcea16b5216e805B0b8caf7784de7570E7, // address of the enabler/disabler of the deposit whitelisting
                      depositorWhitelistRoleHolder: 0xe8616DEcea16b5216e805B0b8caf7784de7570E7,  // address of the depositors whitelister
                      isDepositLimitSetRoleHolder: 0xe8616DEcea16b5216e805B0b8caf7784de7570E7,   // address of the enabler/disabler of the deposit limit
                      depositLimitSetRoleHolder: 0xe8616DEcea16b5216e805B0b8caf7784de7570E7      // address of the deposit limit setter
                   })),
                   delegatorIndex: 0,                                                            // Delegator’s type (= NetworkRestakeDelegator)
                   delegatorParams: abi.encode(INetworkRestakeDelegator.InitParams({
                      baseParams: IBaseDelegator.BaseParams({
                         defaultAdminRoleHolder: 0xe8616DEcea16b5216e805B0b8caf7784de7570E7,     // address of the Delegator’s admin (can manage all roles)
                         hook: 0x0000000000000000000000000000000000000000,                       // address of the hook (if not zero, receives onSlash() call on each slashing)
                         hookSetRoleHolder: 0xe8616DEcea16b5216e805B0b8caf7784de7570E7           // address of the hook setter
                      }),
                      networkLimitSetRoleHolders: networkLimitSetRoleHolders,                    // array of addresses of the network limit setters
                      operatorNetworkSharesSetRoleHolders: operatorNetworkSharesSetRoleHolders   // array of addresses of the operator-network shares setters
                   })),
                   withSlasher: true,                                                            // if enable Slasher module
                   slasherIndex: 1,                                                              // Slasher’s type (= VetoSlasher)
                   slasherParams: abi.encode(IVetoSlasher.InitParams({
                      baseParams: IBaseSlasher.BaseParams({
                         isBurnerHook: true                                                      // if enable the `burner` to receive onSlash() call after each slashing (is needed for the burner router workflow)
                      }),
                      vetoDuration: 86400,                                                       // veto duration (= 1 day)
                      resolverSetEpochsDelay: 3                                                  // number of Vault epochs needed for the resolver to be changed
                   }))
             })
          );
     
          Vault(vault).grantRole(Vault(vault).DEFAULT_ADMIN_ROLE(), 0xe8616DEcea16b5216e805B0b8caf7784de7570E7);
          Vault(vault).grantRole(Vault(vault).DEPOSITOR_WHITELIST_ROLE(), address(this));
     
          IVault(vault).setDepositorWhitelistStatus(<DEPOSITOR_TO_WHITELIST>, true);
     
          Vault(vault).renounceRole(Vault(vault).DEPOSITOR_WHITELIST_ROLE(), address(this));
          Vault(vault).renounceRole(Vault(vault).DEFAULT_ADMIN_ROLE(), address(this));
     
    // ...
Configure core modules with deposit whitelist after deployment
  1. Make sure the caller has a Vault.DEPOSIT_WHITELIST_SET_ROLE() role via the OpenZeppelin’s AccessControl contract
  2. Call Vault.setDepositWhitelist(true)
  3. Make sure the caller has a Vault.DEPOSITOR_WHITELIST_ROLE() role via the OpenZeppelin’s AccessControl contract
  4. Call Vault.setDepositorWhitelistStatus(account, true)