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

If you intend to use Curve's Liquidity Gauge, the CurveLiquidityGaugev3Facet should be connected only after the initial deployment regardless of whether you use DeployVault.sol or DeployVaultWithCoreFacets.s.sol

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.

If you intend to bridge vault shares to another chain, for example, to use in another DeFi protocol, you will need to deploy the OFT on the chain you wish to use it and wire it. See LayerZero docs.

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 build

If 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 (DiamondCutFacet and DiamondLoupeFacet)

  • References to omnichain adapters (LzAdapter and 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

Copy .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:

  1. DeployConfig.s.sol reads your env vars.

  2. getCuts() builds one FacetCut per facet (address, selector array).

  3. DeployVault signs with PRIVATE_KEY and calls deployVault() 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 \
  --broadcast

Save the transaction hash, and once mined, the vault address is printed by Foundry.

Verify

  1. Execute the following command

forge verify-contract \
  --rpc-url $RPC_URL
  --verifier etherscan \
  --etherscan-api-key $API_KEY \
  <address> \
  src/MoreVaultsDiamond.sol:MoreVaultsDiamond \
  1. Wait for verification.

  2. 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
) payable
  • hubEid: 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.

The finalization of your spoke's setup requires block finalization on the spoke chain. For supported chains this can vary between a few minutes and 2 hours. For your spoke deployment transaction, check the block finalization on the respective block scanner.

Bootstrap from the Hub

On the hub chain:

VaultsFactory.requestRegisterSpoke(
    uint32 hubEid,
    address hubVault,
    address spokeVault,
    bytes calldata options
) payable

This 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
) payable

The 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