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.solThe 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-simplify2. Configure the deployment script
Open:
script/DeployVaultV2.s.solUpdate 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;| Parameter | Description |
|---|---|
NAME | Name of the ERC-20 token representing vault shares. |
SYMBOL | Symbol of the ERC-20 token representing vault shares. |
OWNER | Owner of the vault. In the default script, this address also receives all vault and Universal Delegator roles. |
ASSET | ERC-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;| Parameter | Description |
|---|---|
DEPOSIT_LIMIT | Maximum amount of assets allowed in the vault. If set to 0, deposit limits are disabled. |
DEPOSIT_WHITELIST | Whether deposits are restricted to whitelisted depositors. |
DEPOSITOR_TO_WHITELIST | Initial depositor to whitelist. Used only when DEPOSIT_WHITELIST is true. |
The script enables the deposit limit automatically when DEPOSIT_LIMIT != 0:
isDepositLimit: DEPOSIT_LIMIT != 04. 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 holder | Description |
|---|---|
defaultAdminRoleHolder | Address with admin permissions over vault roles. |
managementFeeRoleHolder | Address allowed to manage the vault management fee. |
performanceFeeRoleHolder | Address allowed to manage the vault performance fee. |
depositLimitSetRoleHolder | Address allowed to set the deposit limit. |
depositorWhitelistRoleHolder | Address allowed to whitelist depositors. |
isDepositLimitSetRoleHolder | Address allowed to enable or disable deposit limits. |
depositWhitelistSetRoleHolder | Address 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 holder | Description |
|---|---|
allocateRoleHolder | Address allowed to allocate vault assets through supported adapters. |
deallocateRoleHolder | Address allowed to deallocate assets from adapters. |
forceDeallocateRoleHolder | Address allowed to force deallocation from adapters. |
addAdapterRoleHolder | Address allowed to add new adapters. |
removeAdapterRoleHolder | Address allowed to remove adapters. |
swapAdaptersRoleHolder | Address allowed to swap or reorder adapters. |
setAdapterLimitsRoleHolder | Address allowed to set adapter limits. |
setAutoAllocateAdaptersRoleHolder | Address allowed to configure auto-allocation adapters. |
defaultAdminRoleHolder | Address 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 \
--broadcastReplace:
| Placeholder | Description |
|---|---|
RPC | RPC endpoint for the target network. |
ACCOUNT | Foundry account used for signing. |
SENDER | Sender address used for deployment. |
Example:
forge script script/DeployVaultV2.s.sol:DeployVaultV2Script \
--rpc-url=https://ethereum-rpc.publicnode.com \
--account=deployer \
--sender=0x0000000000000000000000000000000000000000 \
--broadcast7. 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
