Skip to content
LogoLogo

Deploy Vault V2

This guide explains how to deploy a Symbiotic V2 vault using the DeployVaultV2 script.

Vault V2 is an ERC-20 tokenized vault where shares are represented by an ERC-20 token. The vault stores a single asset, manages deposits and withdrawals, and uses a Universal Delegator to allocate assets across supported adapters.

Deployment overview

To deploy a Vault V2, configure and run:

script/DeployVaultV2.s.sol

The script deploys:

  • a Vault V2 contract
  • an ERC-20 vault share token
  • a Universal Delegator linked to the vault
  • optional deposit limits
  • optional depositor whitelisting
  • role holders for vault administration, fee management, deposit controls, allocation, deallocation, and adapter management

The deployment flow uses the Vault Factory from Symbiotic core constants:

SymbioticCoreConstants.core().vaultFactory.create(
    VAULT_V2_VERSION,
    owner,
    abi.encode(vaultParams)
);

After deployment, the script reads the deployed vault's Universal Delegator via:

address delegator = IVaultV2(vault).delegator();

1. Clone the core repository

git clone --recurse-submodules https://github.com/symbioticfi/core.git
cd core
git checkout delegator-simplify

2. Configure the deployment script

Open:

script/DeployVaultV2.s.sol

Update the required fields before deployment:

// Name of the ERC20 representing shares in the vault
string NAME = "SymVault";
 
// Symbol of the ERC20 representing shares in the vault
string SYMBOL = "SV";
 
// Address of the owner of the vault who can migrate the vault to new versions whitelisted by Symbiotic
address OWNER = 0x0000000000000000000000000000000000000000;
 
// Address of the vault asset token
address ASSET = 0x0000000000000000000000000000000000000000;
ParameterDescription
NAMEName of the ERC-20 token representing vault shares.
SYMBOLSymbol of the ERC-20 token representing vault shares.
OWNEROwner of the vault. In the default script, this address also receives all vault and Universal Delegator roles.
ASSETERC-20 asset deposited into the vault.

3. Optional configuration

The script also supports deposit limits and depositor whitelisting.

// Deposit limit (maximum amount of assets allowed in the vault)
uint256 DEPOSIT_LIMIT = 0;
 
// Whether deposits are restricted to whitelisted depositors
bool DEPOSIT_WHITELIST = false;
 
// Initial whitelisted depositor (used only when DEPOSIT_WHITELIST is true)
address DEPOSITOR_TO_WHITELIST = 0x0000000000000000000000000000000000000000;
ParameterDescription
DEPOSIT_LIMITMaximum amount of assets allowed in the vault. If set to 0, deposit limits are disabled.
DEPOSIT_WHITELISTWhether deposits are restricted to whitelisted depositors.
DEPOSITOR_TO_WHITELISTInitial depositor to whitelist. Used only when DEPOSIT_WHITELIST is true.

The script enables the deposit limit automatically when DEPOSIT_LIMIT != 0:

isDepositLimit: DEPOSIT_LIMIT != 0

4. Vault V2 roles

The default script assigns all Vault V2 roles to OWNER.

defaultAdminRoleHolder: OWNER,
managementFeeRoleHolder: OWNER,
performanceFeeRoleHolder: OWNER,
depositLimitSetRoleHolder: OWNER,
depositorWhitelistRoleHolder: OWNER,
isDepositLimitSetRoleHolder: OWNER,
depositWhitelistSetRoleHolder: OWNER,
Role holderDescription
defaultAdminRoleHolderAddress with admin permissions over vault roles.
managementFeeRoleHolderAddress allowed to manage the vault management fee.
performanceFeeRoleHolderAddress allowed to manage the vault performance fee.
depositLimitSetRoleHolderAddress allowed to set the deposit limit.
depositorWhitelistRoleHolderAddress allowed to whitelist depositors.
isDepositLimitSetRoleHolderAddress allowed to enable or disable deposit limits.
depositWhitelistSetRoleHolderAddress allowed to enable or disable depositor whitelisting.

5. Universal Delegator

Vault V2 deploys with a Universal Delegator. The Universal Delegator is encoded in the vault initialization params through delegatorParams.

delegatorParams: abi.encode(
    IUniversalDelegator.InitParams({
        allocateRoleHolder: OWNER,
        deallocateRoleHolder: OWNER,
        forceDeallocateRoleHolder: OWNER,
        addAdapterRoleHolder: OWNER,
        swapAdaptersRoleHolder: OWNER,
        defaultAdminRoleHolder: OWNER,
        removeAdapterRoleHolder: OWNER,
        setAdapterLimitsRoleHolder: OWNER,
        setAutoAllocateAdaptersRoleHolder: OWNER
    })
)

The deployment base validates that the vault and delegator are correctly linked:

assert(IVaultV2(vault).delegator() == delegator);
assert(IUniversalDelegator(delegator).vault() == vault);
Role holderDescription
allocateRoleHolderAddress allowed to allocate vault assets through supported adapters.
deallocateRoleHolderAddress allowed to deallocate assets from adapters.
forceDeallocateRoleHolderAddress allowed to force deallocation from adapters.
addAdapterRoleHolderAddress allowed to add new adapters.
removeAdapterRoleHolderAddress allowed to remove adapters.
swapAdaptersRoleHolderAddress allowed to swap or reorder adapters.
setAdapterLimitsRoleHolderAddress allowed to set adapter limits.
setAutoAllocateAdaptersRoleHolderAddress allowed to configure auto-allocation adapters.
defaultAdminRoleHolderAddress with admin permissions over Universal Delegator roles.

6. Deploy the vault

Run the deployment script with Foundry:

forge script script/DeployVaultV2.s.sol:DeployVaultV2Script \
  --rpc-url=RPC \
  --account=ACCOUNT \
  --sender=SENDER \
  --broadcast

Replace:

PlaceholderDescription
RPCRPC endpoint for the target network.
ACCOUNTFoundry account used for signing.
SENDERSender address used for deployment.

Example:

forge script script/DeployVaultV2.s.sol:DeployVaultV2Script \
  --rpc-url=https://ethereum-rpc.publicnode.com \
  --account=deployer \
  --sender=0x0000000000000000000000000000000000000000 \
  --broadcast

7. Deployment output

After deployment, the script logs the deployed Vault V2 and Universal Delegator addresses:

Logs.log(
    string.concat(
        "Deployed VaultV2",
        "\n    vault:",
        vm.toString(vault_),
        "\n    delegator:",
        vm.toString(delegator_)
    )
);

8. Complete deployment script

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;
 
import "./base/DeployVaultV2Base.sol";
 
// forge script script/DeployVaultV2.s.sol:DeployVaultV2Script --rpc-url=RPC --account=ACCOUNT --sender=SENDER --broadcast
 
contract DeployVaultV2Script is DeployVaultV2Base {
    // Configurations - UPDATE THESE BEFORE DEPLOYMENT
 
    // Name of the ERC20 representing shares in the vault
    string NAME = "SymVault";
    // Symbol of the ERC20 representing shares in the vault
    string SYMBOL = "SV";
    // Address of the owner of the vault who can migrate the vault to new versions whitelisted by Symbiotic
    address OWNER = 0x0000000000000000000000000000000000000000;
    // Address of the vault asset token
    address ASSET = 0x0000000000000000000000000000000000000000;
 
    // Optional
 
    // Deposit limit (maximum amount of assets allowed in the vault)
    uint256 DEPOSIT_LIMIT = 0;
    // Whether deposits are restricted to whitelisted depositors
    bool DEPOSIT_WHITELIST = false;
    // Initial whitelisted depositor (used only when DEPOSIT_WHITELIST is true)
    address DEPOSITOR_TO_WHITELIST = 0x0000000000000000000000000000000000000000;
 
    function run() public {
        runBase(
            DeployVaultV2Params({
                owner: OWNER,
                vaultParams: VaultV2Params({
                    baseParams: IVaultV2.InitParams({
                        name: NAME,
                        symbol: SYMBOL,
                        asset: ASSET,
                        depositWhitelist: DEPOSIT_WHITELIST,
                        depositorToWhitelist: DEPOSITOR_TO_WHITELIST,
                        depositLimit: DEPOSIT_LIMIT,
                        isDepositLimit: DEPOSIT_LIMIT != 0,
                        defaultAdminRoleHolder: OWNER,
                        managementFeeRoleHolder: OWNER,
                        performanceFeeRoleHolder: OWNER,
                        depositLimitSetRoleHolder: OWNER,
                        depositorWhitelistRoleHolder: OWNER,
                        isDepositLimitSetRoleHolder: OWNER,
                        depositWhitelistSetRoleHolder: OWNER,
                        delegatorParams: abi.encode(
                            IUniversalDelegator.InitParams({
                                allocateRoleHolder: OWNER,
                                deallocateRoleHolder: OWNER,
                                forceDeallocateRoleHolder: OWNER,
                                addAdapterRoleHolder: OWNER,
                                swapAdaptersRoleHolder: OWNER,
                                defaultAdminRoleHolder: OWNER,
                                removeAdapterRoleHolder: OWNER,
                                setAdapterLimitsRoleHolder: OWNER,
                                setAutoAllocateAdaptersRoleHolder: OWNER
                            })
                        )
                    })
                })
            })
        );
    }
}

9. Complete base script

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;
 
import {Script} from "forge-std/Script.sol";
 
import {IUniversalDelegator} from "../../src/interfaces/delegator/IUniversalDelegator.sol";
import {IVaultV2, VAULT_V2_VERSION} from "../../src/interfaces/vault/IVaultV2.sol";
import {Logs} from "../utils/Logs.sol";
import {SymbioticCoreConstants} from "../../test/integration/SymbioticCoreConstants.sol";
 
contract DeployVaultV2Base is Script {
    struct VaultV2Params {
        IVaultV2.InitParams baseParams;
    }
 
    struct DeployVaultV2Params {
        address owner;
        VaultV2Params vaultParams;
    }
 
    function runBase(DeployVaultV2Params memory params) public returns (address, address) {
        vm.startBroadcast();
 
        address vault_ = address(
            SymbioticCoreConstants.core().vaultFactory
                .create(_getVaultVersion(), params.owner, _getVaultParamsEncoded(params))
        );
        address delegator_ = IVaultV2(vault_).delegator();
 
        Logs.log(
            string.concat(
                "Deployed VaultV2", "\n    vault:", vm.toString(vault_), "\n    delegator:", vm.toString(delegator_)
            )
        );
 
        _validateDeployment(vault_, delegator_);
 
        vm.stopBroadcast();
        return (vault_, delegator_);
    }
 
    function _getVaultVersion() internal virtual returns (uint64) {
        return VAULT_V2_VERSION;
    }
 
    function _getVaultParamsEncoded(DeployVaultV2Params memory params) internal pure virtual returns (bytes memory) {
        return abi.encode(params.vaultParams.baseParams);
    }
 
    function _validateDeployment(address vault, address delegator) internal view {
        assert(IVaultV2(vault).delegator() == delegator);
        assert(IUniversalDelegator(delegator).vault() == vault);
    }
}

Next steps

After deployment, the vault owner can configure the vault by:

  • setting or updating deposit limits
  • enabling or disabling depositor whitelisting
  • whitelisting depositors
  • adding adapters
  • removing adapters
  • swapping adapters
  • configuring adapter limits
  • configuring auto-allocation adapters
  • allocating and deallocating assets through the Universal Delegator