Manage Adapters (Vaults V2)
Adapters are the core of a Morpho Vault V2's extensibility. They are smart contracts that act as bridges, allowing your vault to connect to and allocate assets in any external yield protocol. As a Curator
, enabling and managing adapters is how you define your vault's investment universe.
This guide will walk you through the process of deploying, listing, and managing adapters, with a focus on two key adapters:
MorphoVaultV1Adapter
: Connects your Vault V2 to a Morpho Vault V1 (MetaMorpho).MorphoMarketV1Adapter
: Connects your Vault V2 directly to Morpho Blue markets.
Setting Up Your First Adapter: The MorphoVaultV1Adapter
A common and powerful strategy is to use a Vault V2 as a "wrapper" around an existing Morpho Vault V1. This is automatically handled by the deployment script.
Method 1: Setting an Adapter via Script (Recommended)
The official deployment repository handles adapter setup automatically: https://github.com/morpho-org/vault-v2-deployment
The DeployVaultV2.s.sol
script includes:
// Deploy MorphoVaultV1Adapter
address morphoVaultV1Adapter = address(new MorphoVaultV1Adapter(address(vaultV2), address(vaultV1)));
// Submit and execute adapter enablement
bytes memory idData = abi.encode("this", morphoVaultV1Adapter);
vaultV2.submit(abi.encodeCall(vaultV2.setIsAdapter, (morphoVaultV1Adapter, true)));
vaultV2.setIsAdapter(morphoVaultV1Adapter, true);
// Set caps for the adapter
vaultV2.submit(abi.encodeCall(vaultV2.increaseAbsoluteCap, (idData, type(uint128).max)));
vaultV2.submit(abi.encodeCall(vaultV2.increaseRelativeCap, (idData, 1e18)));
vaultV2.increaseAbsoluteCap(idData, type(uint128).max);
vaultV2.increaseRelativeCap(idData, 1e18);
// Set as liquidity adapter
vaultV2.setLiquidityAdapterAndData(morphoVaultV1Adapter, bytes(""));
For adding adapters after deployment:
script/AddAdapter.s.sol
pragma solidity 0.8.28;
import {Script, console} from "forge-std/Script.sol";
import {IVaultV2} from "vault-v2/interfaces/IVaultV2.sol";
import {MorphoVaultV1Adapter} from "vault-v2/adapters/MorphoVaultV1Adapter.sol";
import {MorphoMarketV1Adapter} from "vault-v2/adapters/MorphoMarketV1Adapter.sol";
contract AddAdapter is Script {
function run() external {
address vaultAddress = vm.envAddress("VAULT_V2_ADDRESS");
address targetVaultV1 = vm.envAddress("TARGET_VAULT_V1");
IVaultV2 vault = IVaultV2(vaultAddress);
vm.startBroadcast();
// Deploy new adapter
address newAdapter = address(new MorphoVaultV1Adapter(vaultAddress, targetVaultV1));
// Submit enablement proposal
bytes memory enableData = abi.encodeCall(IVaultV2.setIsAdapter, (newAdapter, true));
vault.submit(enableData);
// Submit cap increases
bytes memory idData = abi.encode("this", newAdapter);
vault.submit(abi.encodeCall(IVaultV2.increaseAbsoluteCap, (idData, type(uint128).max)));
vault.submit(abi.encodeCall(IVaultV2.increaseRelativeCap, (idData, 1e18)));
vm.stopBroadcast();
console.log("Adapter deployed at:", newAdapter);
console.log("Proposals submitted. Execute after timelock.");
}
}
Method 2: Setting an Adapter via Etherscan
1. Deploy the Adapter
For MorphoVaultV1Adapter
:
- Deploy the contract with constructor parameters:
_parentVault
: Your Vault V2 address_morphoVaultV1
: Target Vault V1 address
For MorphoMarketV1Adapter
:
- Deploy with:
_parentVault
: Your Vault V2 address_morpho
: Morpho Blue contract address
2. Enable the Adapter
- As Curator, encode:
abi.encodeCall(IVaultV2.setIsAdapter, (adapterAddress, true))
. - Call
submit(bytes)
with the encoded data. - After timelock, call
setIsAdapter(adapterAddress, true)
.
3. Set Risk Caps
The adapter's ID is computed as:
bytes memory idData = abi.encode("this", adapterAddress);
bytes32 id = keccak256(idData);
Submit and execute cap increases:
increaseAbsoluteCap(idData, capAmount)
increaseRelativeCap(idData, relativeCapWad)
(where 1e18 = 100%)
4. Set as Liquidity Adapter (if needed)
As Allocator, call:
setLiquidityAdapterAndData(adapterAddress, bytes(""))
Working with MorphoMarketV1Adapter
When allocating to Morpho Blue markets directly:
// Prepare market parameters
MarketParams memory marketParams = MarketParams({
loanToken: loanTokenAddress,
collateralToken: collateralTokenAddress,
oracle: oracleAddress,
irm: irmAddress,
lltv: lltv
});
// Allocate as Allocator
bytes memory data = abi.encode(marketParams);
vault.allocate(morphoMarketV1Adapter, data, amount);
Managing Adapter Caps
Each adapter has associated IDs for risk management:
- MorphoVaultV1Adapter: Single ID based on
keccak256(abi.encode("this", adapterAddress))
- MorphoMarketV1Adapter: Three IDs:
- Adapter ID:
keccak256(abi.encode("this", adapterAddress))
- Collateral ID:
keccak256(abi.encode("collateralToken", collateralAddress))
- Market ID:
keccak256(abi.encode("this/marketParams", adapterAddress, marketParams))
- Adapter ID:
Sentinels can decrease caps immediately:
vault.decreaseAbsoluteCap(idData, newLowerCap);
vault.decreaseRelativeCap(idData, newLowerRelativeCap);
Delisting an Adapter
To delist:
- Deallocate All Funds:
vault.deallocate(adapterAddress, data, totalAllocated);
- Submit Delisting Proposal (as Curator):
bytes memory data = abi.encodeCall(IVaultV2.setIsAdapter, (adapterAddress, false));
vault.submit(data);
- Execute After Timelock:
vault.setIsAdapter(adapterAddress, false);
Setting Force Deallocate Penalty
To enable emergency withdrawals with a penalty:
// As Curator, submit proposal (max 2% = 0.02e18)
vault.submit(abi.encodeCall(IVaultV2.setForceDeallocatePenalty, (adapterAddress, 0.01e18)));
// After timelock
vault.setForceDeallocatePenalty(adapterAddress, 0.01e18); // 1% penalty