Deploying Vaults
This walkthrough shows how a strategist can launch a MORE Vault on any EVM network on which the MORE Vaults protocol is deployed using the Solidity scripts that ship with the core repo. To deploy on your target network, you can switch RPC endpoints and explorer links.
Quick links:
There is a single registry on each chain's MORE Vault Factory. The registry is permissionless insofar as anyone can create a vault. However, vaults may only activate facets included in the Vault Registry.
Cross-Chain Hub & Spoke Model
While MORE vaults can be deployed as single chain vaults, the protocol was designed to support omnichain deployments out-of-the-box.
To facilitate omnichain vaults, MORE relies on a hub and spoke model, supported by the core bridge facet. The facet itself is agnostic to underlying bridges and provides a unified interface with any cross-chain messaging infrastructure.
The flagship version of this facet ships with support for LayerZero. Later, it will be extended to include additional bridges with the goal of offering additional breadth and fallback resilience.
Step-by-step deployment guide
There are multiple scripts that can be used to deploy both the Core Vault as well as certain integrations, known as facets.
DeployVault.s.sol – deploys the Core Vault as well as all available and whitelisted integrations.
DeployVaultWithCoreFacets.s.sol – deploys only the Core Vault with core facets.
Other scripts exist to deploy individual integrations such as Aave v3, Curve, Uniswap v3, etc.
Scripts location: /scripts
What you’ll deploy
Diamond proxy – the vault contract which uses existing core and optional facets.
MORE Vaults Composer – handles cross-chain actions within an omnichain vault deployment.
OFT Adapter for Vault Shares - smart contract that handles bridging shares of spoke vaults to the hub.
Environment setup
# Install Foundry
curl -L https://foundry.paradigm.xyz | bash && foundryup
# Clone the repo
git clone https://github.com/MORE-Vaults/More-Vaults-Periphery/
cd More-Vaults-Periphery
# Compile once
forge buildIf forge build fails or the RPC later errors, hop onto Discord and open a #support-ticket for live help.
Deploying the Hub Vault
The hub vault is the root of an omnichain vault network. It acts as the canonical authority for global NAV (Net Asset Value), cross‑chain coordination, and curator actions. While factories and facets are deployed by the protocol, the curator initializes and configures the hub to manage strategies and coordinate spokes.
When the hub is deployed, it contains:
Core ERC‑4626 logic (
VaultFacet)Cross‑chain logic (
BridgeFacet)Access control configuration (
AccessControlFacet)Vault configuration facet (
ConfigurationFacet)Default Diamond facets (
DiamondCutFacetandDiamondLoupeFacet)References to omnichain adapters (
LzAdapterand OFT adapters)Multicall facet
ERC-4626 and ERC-7540 logic to interact with other vaults
From the curator’s point of view, deploying the hub establishes the control center for all cross‑chain actions.
Create .env
.envCopy .env.example to .env and fill in:
# GENERAL PARAMS
PRIVATE_KEY="Your Private Key"
# VAULT CREATION PARAMS
OWNER="The owner's EVM address"
CURATOR="The curator's EVM address"
GUARDIAN="The guardian's EVM address"
FEE_RECIPIENT="The fee recipient's EVM address"
UNDERLYING_ASSET="The EVM token address of the underlying asset"
FEE=1000 # In BPS, 0 means no performance fees will be taken
DEPOSIT_CAPACITY=10000000 # Underlying asset's value with decimals
TIME_LOCK_PERIOD=86400 # In seconds
MAX_SLIPPAGE_PERCENT=1000 # BPS
VAULT_NAME="Your vault name"
VAULT_SYMBOL="Your vault's ticker"
IS_HUB="If this is set to TRUE, the vault will be enabled for deposits and withdrawals. If this is set ot false, the vault will serve as a spoke and only the hub vault will be able to deposit and withdraw."
SALT="Any unique value. The transaction will revert if the value was already used on the same chain."
# DEPLOYED REQUIRED PROTOCOL ADDRESSES
DIAMOND_CUT_FACET=0x0629d67cba46438458e96E7Fd7BD46AFe6F38ee7
DIAMOND_LOUPE_FACET=0xBfb5bf7129D80c582681E5f59aA21Ba23834E708
ACCESS_CONTROL_FACET=0xfdf1C242E8E9847f2edEBaB3c0f3bE5f85EeD38C
CONFIGURATION_FACET=0x475d696B75fD49f48CD1D8a4389C7aD755891441
VAULT_FACET=0xe405e2FEC812Bd73548e75c2544cfd176Bdb8878
MULTICALL_FACET=0x4c25db05c999081cdb24AdFdD9cD871f70d998E3
ERC4626_FACET=0xc5c6844fE3a550748cAaEAf8592d68386ca1f1B5
ERC7540_FACET=0x5b49fb340eE2A92ac9B5AE9A6920A54911b5633B
BRIDGE_FACET=0xd08cAB25309DFeA0A48dB8E9ef3d5aFA58cd37bB
VAULT_REGISTRY=0x6a0B3724AF49Ce6f14669D07823650Ec26553890
VAULTS_FACTORY=0x7bDB8B17604b03125eFAED33cA0c55FBf856BB0C
# You can find these addresses on the Contracts page.
# DEPLOYED OPTIONAL PROTOCOL ADDRESSES
MORE_LEVERAGE_FACET=0x589cCdAf387E265423c1d2f95cdc903fDFdA5fc3
AAVE_V3_FACET=0x3172c30821D61B97Ed0c9B21C0fe42ff0b362fbD
CURVE_FACET=0x00f8AbFe17B4c096440a647Bb0549F326e08c897
UNISWAP_V3_FACET=0x3df5923afB843fdc530C144844C994db8E59B5aD
MULTI_REWARDS_FACET=0x65c89a8aEF485D3da46ED3EE20BF9D59e4D6Cd0f
# DEPLOYED OPTIONAL PROTOCOL ADDRESSES THAT REQUIRE ADDITIONAL CONFIGURATION
## CURVE GAUGE V6 FACET
CURVE_GAUGE_V6_FACET=0x4fc8DFC9A4AcE779e78591B17B83ea1988fF3Aa1
CURVE_MINTER=
# Varies by chain, but available on Curve's docs and smart contracts
# You can find these addresses on the Contracts page.Need facet addresses? The Contracts page lists every DAO‑approved facet.
Dry-run
forge script scripts/DeployVault.s.sol:DeployVault \
--rpc-url $RPC_URL --chain-id $CHAIN_ID \Foundry prints the CreateVaultParams and the array of FacetCut entries it will submit. Check every field: roles, fee, capacity, facet list. Edit .env and repeat until correct.
What happens:
DeployConfig.s.solreads your env vars.getCuts()builds oneFacetCutper facet (address, selector array).DeployVaultsigns withPRIVATE_KEYand callsdeployVault()on the factory.
Broadcast
Add --broadcast to send the transaction:
forge script scripts/DeployVault.s.sol:DeployVault \
--rpc-url $RPC_URL --chain-id $CHAIN_ID \
--broadcastSave the transaction hash, and once mined, the vault address is printed by Foundry.
Verify
Execute the following command
forge verify-contract \
--rpc-url $RPC_URL
--verifier etherscan \
--etherscan-api-key $API_KEY \
<address> \
src/MoreVaultsDiamond.sol:MoreVaultsDiamond \Wait for verification.
Check the returned URL and ensure your contract was verified.
Execute the following command
forge verify-contract \
--rpc-url https://mainnet.evm.nodes.onflow.org/ \
--verifier blockscout \
--verifier-url 'https://evm.flowscan.io/api/' \
<address> \
src/MoreVaultsDiamond.sol:MoreVaultsDiamond \Wait for verification.
Check the returned URL and ensure your contract was verified.
Deploying Spoke Vaults and Building the Mesh
Spoke vaults are the chain‑local execution units. Each spoke manages assets on its chain but reports accounting back to the hub. Together, the hub and spokes form a mesh, where the hub commands and spokes respond.
Spokes rely on the same diamond architecture but are initialized with isHub = FALSE. The curator’s task is to ensure that every spoke is registered and connected to the hub. Use the same salt for each chain. For easier management and compatibility, ensure facets match those of the hub.
Request Spoke Registration
Execute this from the spoke chain:
VaultsFactory.requestRegisterSpoke(
uint32 hubEid,
address hubVault,
address spokeVault,
bytes calldata options
) payablehubEid: LayerZero EID for the hub chain.hubVault: Address of the hub.spokeVault: Address of the new spoke.options: LayerZero executor options (gas limit, refund address).msg.value: Covers LayerZero message fee.
The function emits SpokeRegistrationRequested, notifying the hub.
Bootstrap from the Hub
On the hub chain:
VaultsFactory.requestRegisterSpoke(
uint32 hubEid,
address hubVault,
address spokeVault,
bytes calldata options
) payableThis transmits initialization data and configuration to the spoke.
Broadcast the Full Mesh
If you have deployed more than 1 spoke:
VaultsFactory.hubBroadcastSpokeAdded(
address hubVault,
uint32 newSpokeEid,
address newSpokeVault,
uint32[] calldata dstEids,
bytes calldata options
) payableThe hub notifies all other spokes of the new connections, ensuring they’re aware of each other.
Verify Connectivity
After bootstrapping:
VaultsFactory.hubToSpokes(hubEid, hubVault);
VaultsFactory.isSpokeOfHub(hubEid, hubVault, spokeEid, spokeVault);
VaultsFactory.spokeToHub(spokeEid, spokeVault);All should return valid data. A missing entry means registration failed or LayerZero messaging didn’t complete.
Last updated