Skip to content

Morpho Vault

Field Value
Module almanak.connectors.morpho_vault
Protocol kind Vault
Aliases N/A

Supported Chains And Intents

Chain Family Supported Intents
Base EVM VAULT_DEPOSIT, VAULT_REDEEM
Ethereum EVM VAULT_DEPOSIT, VAULT_REDEEM

morpho_vault

MetaMorpho Vault Connector.

This module provides adapters and utilities for interacting with MetaMorpho vaults, the ERC-4626 vault layer that aggregates capital across Morpho Blue lending markets.

MetaMorpho Features: - ERC-4626 compliant vault deposits and redemptions - Passive yield optimization with curator-managed allocation - Multi-market capital allocation across Morpho Blue markets - Transparent share pricing via convertToAssets/convertToShares

Supported Chains: - Ethereum - Base

Example
from almanak.connectors.morpho_vault import (
    MetaMorphoAdapter,
    MetaMorphoConfig,
    MetaMorphoReceiptParser,
    MetaMorphoSDK,
    create_test_adapter,
)
from decimal import Decimal

# Initialize adapter
config = MetaMorphoConfig(chain="ethereum", wallet_address="0x...")
adapter = MetaMorphoAdapter(config, gateway_client=gateway_client)

# Deposit assets
result = adapter.deposit(
    vault_address="0xBEEF01735c132Ada46AA9aA4c54623cAA92A64CB",
    amount=Decimal("1000"),
)

# Redeem all shares
result = adapter.redeem(
    vault_address="0xBEEF01735c132Ada46AA9aA4c54623cAA92A64CB",
    shares="all",
)

# Parse transaction receipts
parser = MetaMorphoReceiptParser()
parse_result = parser.parse_receipt(receipt)

MetaMorphoAdapter

MetaMorphoAdapter(
    config: MetaMorphoConfig,
    gateway_client=None,
    token_resolver: TokenResolver | None = None,
)

Adapter for MetaMorpho vault protocol.

Provides high-level methods for depositing into and redeeming from MetaMorpho ERC-4626 vaults, with token resolution and validation.

Example
config = MetaMorphoConfig(chain="ethereum", wallet_address="0x...")
adapter = MetaMorphoAdapter(config, gateway_client=client)

# Get vault info
info = adapter.get_vault_info("0xBEEF...")

# Deposit
result = adapter.deposit("0xBEEF...", Decimal("1000"))

Initialize the adapter.

Parameters:

Name Type Description Default
config MetaMorphoConfig

Adapter configuration

required
gateway_client

Gateway client for RPC calls. Required for on-chain operations.

None
token_resolver TokenResolver | None

Optional TokenResolver instance. If None, uses singleton.

None

sdk property

sdk: MetaMorphoSDK

Get the SDK instance (lazy initialization).

get_vault_info

get_vault_info(vault_address: str) -> VaultInfo

Get complete vault information.

Parameters:

Name Type Description Default
vault_address str

MetaMorpho vault address

required

Returns:

Type Description
VaultInfo

VaultInfo with vault state

get_position

get_position(
    vault_address: str, user: str | None = None
) -> VaultPosition

Get user's position in the vault.

Parameters:

Name Type Description Default
vault_address str

MetaMorpho vault address

required
user str | None

User address (defaults to wallet_address)

None

Returns:

Type Description
VaultPosition

VaultPosition with shares and assets

deposit

deposit(
    vault_address: str, amount: Decimal
) -> TransactionResult

Build a deposit transaction for a MetaMorpho vault.

This builds approve + deposit transactions. The approve TX authorizes the vault to pull the exact amount of underlying tokens.

Parameters:

Name Type Description Default
vault_address str

MetaMorpho vault address

required
amount Decimal

Amount of underlying assets to deposit (in token units, e.g. 1000.0 USDC)

required

Returns:

Type Description
TransactionResult

TransactionResult with transaction data for both approve and deposit

redeem

redeem(
    vault_address: str,
    shares: Decimal | str,
    *,
    allow_force_deallocate: bool = False,
    max_force_deallocate_penalty_bps: int = 10,
) -> TransactionResult

Build a redeem transaction for a Morpho vault (v1 or V2).

Parameters:

Name Type Description Default
vault_address str

Morpho vault address

required
shares Decimal | str

Number of shares to redeem, or "all" to redeem all

required
allow_force_deallocate bool

V2 only — permit a penalised forced exit (forceDeallocate legs before the redeem) when the vault's liquidity market cannot cover it. Default False: fail closed.

False
max_force_deallocate_penalty_bps int

V2 only — refuse a forced exit whose penalty exceeds this share of the redeemed assets.

10

Returns:

Type Description
TransactionResult

TransactionResult with transaction data

build_approve_transaction

build_approve_transaction(
    token: str, amount: Decimal, spender: str
) -> TransactionResult

Build an ERC20 approve transaction.

Parameters:

Name Type Description Default
token str

Token symbol or address

required
amount Decimal

Amount to approve

required
spender str

Address to approve

required

Returns:

Type Description
TransactionResult

TransactionResult with transaction data

MetaMorphoConfig dataclass

MetaMorphoConfig(chain: str, wallet_address: str)

Configuration for MetaMorpho adapter.

Attributes:

Name Type Description
chain str

Blockchain network (ethereum, base)

wallet_address str

User wallet address

TransactionResult dataclass

TransactionResult(
    success: bool,
    tx_data: dict[str, Any] | None = None,
    gas_estimate: int = 0,
    description: str = "",
    error: str | None = None,
    requires_atomic: bool = False,
)

Result of a transaction build operation.

Attributes:

Name Type Description
success bool

Whether operation succeeded

tx_data dict[str, Any] | None

Transaction payloads only (to/value/data). Execution order is insertion order; force-deallocate legs come before redeem.

gas_estimate int

Estimated gas

description str

Human-readable description

error str | None

Error message if failed

requires_atomic bool

When True, the caller must put this on ActionBundle.metadata['requires_atomic'] so the orchestrator can refuse EOA sequential confirm. Not a transaction payload.

MetaMorphoEvent dataclass

MetaMorphoEvent(
    event_type: MetaMorphoEventType,
    event_name: str,
    log_index: int,
    transaction_hash: str,
    block_number: int,
    contract_address: str,
    data: dict[str, Any],
    raw_topics: list[str] = list(),
    raw_data: str = "",
)

Parsed MetaMorpho event.

MetaMorphoEventType

Bases: Enum

MetaMorpho event types.

MetaMorphoReceiptParser

MetaMorphoReceiptParser(**kwargs: Any)

Parser for MetaMorpho vault transaction receipts.

Extracts ERC-4626 Deposit/Withdraw events and ERC-20 Transfer/Approval events.

Example

parser = MetaMorphoReceiptParser() result = parser.parse_receipt(receipt) if result.success: for event in result.events: print(f"{event.event_name}: {event.data}")

Initialize the parser.

parse_receipt

parse_receipt(
    receipt: dict[str, Any],
    timestamp: datetime | None = None,
) -> ParseResult

Parse a transaction receipt.

Parameters:

Name Type Description Default
receipt dict[str, Any]

Transaction receipt dictionary with 'logs' field

required
timestamp datetime | None

Optional timestamp for events

None

Returns:

Type Description
ParseResult

ParseResult with parsed events

extract_deposit_data

extract_deposit_data(
    receipt: dict[str, Any],
) -> dict | None

Extract deposit data from transaction receipt.

Called by ResultEnricher for VAULT_DEPOSIT intents.

Returns:

Type Description
dict | None

Dict with {assets, shares, share_price_raw} if found, None otherwise.

dict | None

All values are in raw on-chain units (wei). share_price_raw is the

dict | None

ratio of raw assets to raw shares -- to get a human-readable price,

dict | None

normalize by asset and share decimals.

extract_redeem_data

extract_redeem_data(receipt: dict[str, Any]) -> dict | None

Extract redeem data from transaction receipt.

Called by ResultEnricher for VAULT_REDEEM intents. The redemption is the Withdraw that pays an external receiver; penalty Withdraws (receiver == vault, emitted by forceDeallocate) are reported separately as penalty_shares / penalty_assets and NEVER as the payout. A receipt holding only penalty legs yields None so the enricher moves on to the receipt that carries the redemption.

Returns:

Type Description
dict | None

Dict with {shares_burned, assets_received, penalty_shares, penalty_assets}

dict | None

if a payout Withdraw is found, None otherwise.

extract_force_deallocate_penalty

extract_force_deallocate_penalty(
    receipt: dict[str, Any],
) -> dict | None

Sum the forceDeallocate penalty legs (Withdraws paid to the vault itself) in a receipt.

Returns:

Type Description
dict | None

Dict with {penalty_shares, penalty_assets} when at least one penalty

dict | None

leg is present, None otherwise.

ParseResult dataclass

ParseResult(
    success: bool,
    events: list[MetaMorphoEvent] = list(),
    error: str | None = None,
    transaction_hash: str = "",
    block_number: int = 0,
)

Result of parsing a transaction receipt.

TransferEventData dataclass

TransferEventData(
    from_address: str, to_address: str, amount: Decimal
)

Parsed data from ERC-20 Transfer event.

VaultDepositEventData dataclass

VaultDepositEventData(
    sender: str,
    owner: str,
    assets: Decimal,
    shares: Decimal,
)

Parsed data from ERC-4626 Deposit event.

VaultWithdrawEventData dataclass

VaultWithdrawEventData(
    sender: str,
    receiver: str,
    owner: str,
    assets: Decimal,
    shares: Decimal,
)

Parsed data from ERC-4626 Withdraw event.

DepositExceedsCapError

Bases: MetaMorphoSDKError

Raised when deposit amount exceeds vault's maxDeposit.

InsufficientSharesError

Bases: MetaMorphoSDKError

Raised when redeem amount exceeds user's redeemable shares.

MetaMorphoSDK

MetaMorphoSDK(gateway_client, chain: str)

Low-level SDK for reading MetaMorpho vault state via gateway RPC calls.

All RPC calls are routed through the gateway client's RPC service. This SDK handles ABI encoding/decoding and provides typed return values.

Parameters:

Name Type Description Default
gateway_client

Connected gateway client with RPC service

required
chain str

Chain identifier (e.g., "ethereum", "base")

required

detect_vault_version

detect_vault_version(vault_address: str) -> str

Fingerprint the vault generation on-chain (cached per address).

withdrawQueueLength() answers only on MetaMorpho v1; adaptersLength() answers only on Morpho Vault V2. A contract that answers neither is not a Morpho vault this connector knows how to exit safely — UnsupportedVaultError rather than a guess.

get_vault_asset

get_vault_asset(vault_address: str) -> str

Read the vault's underlying asset address (asset()).

get_total_assets

get_total_assets(vault_address: str) -> int

Read the vault's total assets (totalAssets()).

get_total_supply

get_total_supply(vault_address: str) -> int

Read the vault's total share supply (totalSupply()).

get_share_price

get_share_price(vault_address: str) -> int

Get share price as convertToAssets(one_share) in raw underlying units.

get_decimals

get_decimals(vault_address: str) -> int

Read the vault's share decimals (decimals()). Always 18 for MetaMorpho.

get_balance_of

get_balance_of(vault_address: str, user: str) -> int

Read user's share balance in the vault.

get_max_deposit

get_max_deposit(vault_address: str, receiver: str) -> int

Read maximum deposit amount allowed for a receiver.

get_max_redeem

get_max_redeem(vault_address: str, owner: str) -> int

Read maximum shares that can be redeemed by an owner.

preview_deposit

preview_deposit(vault_address: str, assets: int) -> int

Preview how many shares a deposit of assets would mint.

preview_redeem

preview_redeem(vault_address: str, shares: int) -> int

Preview how many assets a redemption of shares would return.

convert_to_assets

convert_to_assets(vault_address: str, shares: int) -> int

Convert share amount to asset amount.

convert_to_shares

convert_to_shares(vault_address: str, assets: int) -> int

Convert asset amount to share amount.

get_redeemable_shares

get_redeemable_shares(
    vault_address: str, owner: str
) -> int

Shares a redeem-all should request, by generation.

v1: maxRedeem(owner) — a hair below balanceOf because of the ERC-4626 round-trip rounding; redeem(balanceOf) reverts there. V2: balanceOf(owner) — V2 returns 0 from every max* view by design, so maxRedeem would turn every exit into "No shares to redeem". Liquidity is checked by simulate_redeem.

simulate_redeem

simulate_redeem(
    vault_address: str,
    shares: int,
    receiver: str,
    owner: str,
) -> int

Dry-run redeem(shares, receiver, owner) as an eth_call from owner.

Returns the assets the redeem would return. Raises VaultIlliquidError when the call reverts — on V2 that is the "idle + liquidity adapter cannot cover this" signal (or a gate refusal); the tx must not be sent.

check_deposit_gate

check_deposit_gate(
    vault_address: str,
    sender: str,
    receiver: str | None = None,
) -> None

V2 only: raise VaultGatedError if the deposit is gated.

A V2 vault can configure sendAssetsGate independently of receiveSharesGate: the depositing wallet (sender) must be allowed to supply the underlying, and receiver must be allowed to receive shares. receiver defaults to sender.

check_redeem_gates

check_redeem_gates(
    vault_address: str, owner: str, receiver: str
) -> None

V2 only: raise VaultGatedError if owner may not send shares or receiver receive assets.

get_v2_liquidity_data

get_v2_liquidity_data(vault_address: str) -> str | None

The vault's liquidityData() — abi.encode(MarketParams) of the liquidity market.

get_v2_adapter_market_ids

get_v2_adapter_market_ids(adapter: str) -> list[str]

Enumerate marketIds(i) on a MorphoMarketV1Adapter until it reverts.

get_v2_adapter_morpho

get_v2_adapter_morpho(adapter: str) -> str

The Morpho Blue singleton the adapter allocates to.

get_morpho_blue_market_params_data

get_morpho_blue_market_params_data(
    morpho: str, market_id: str
) -> str

idToMarketParams(id) as raw 160-byte hex — the adapter's data for that market.

get_morpho_blue_market_liquidity

get_morpho_blue_market_liquidity(
    morpho: str, market_id: str
) -> tuple[int, int, int]

market(id) → (totalSupplyAssets, totalSupplyShares, available liquidity).

get_morpho_blue_supply_shares

get_morpho_blue_supply_shares(
    morpho: str, market_id: str, account: str
) -> int

position(id, account).supplyShares.

get_asset_balance_of

get_asset_balance_of(token: str, account: str) -> int

ERC-20 balanceOf of account on token (the vault's idle assets when account is the vault).

preview_withdraw

preview_withdraw(vault_address: str, assets: int) -> int

previewWithdraw(assets) — shares burned to withdraw assets (rounds up).

plan_force_deallocate

plan_force_deallocate(
    vault_address: str,
    shares: int,
    owner: str,
    max_penalty_bps: int,
) -> ForceDeallocatePlan

Size a forced exit for redeeming shares against live state.

Shortfall = assets the redeem needs − vault idle assets − what the vault's own liquidity market can serve (the normal path already drains that one). The shortfall (+ a 0.1% accrual buffer) is covered from the adapters' OTHER markets, largest withdrawable first, where withdrawable is min(adapter's supplied assets there, that market's free liquidity). Refused whole when the markets cannot cover it or the total penalty exceeds max_penalty_bps of the redeemed assets.

build_force_deallocate_tx

build_force_deallocate_tx(
    vault_address: str,
    adapter: str,
    market_params_data: str,
    assets: int,
    on_behalf: str,
) -> dict

Unsigned forceDeallocate call (sent by on_behalf, who pays the penalty).

simulate_force_deallocate

simulate_force_deallocate(
    vault_address: str,
    adapter: str,
    market_params_data: str,
    assets: int,
    on_behalf: str,
) -> None

Dry-run one forceDeallocate leg from on_behalf; raises VaultIlliquidError on revert.

get_curator

get_curator(vault_address: str) -> str

Read the vault's curator address.

get_fee

get_fee(vault_address: str) -> int

Read the vault's performance fee (WAD scale, 1e18 = 100%).

get_timelock

get_timelock(vault_address: str) -> int

Read the vault's timelock duration in seconds.

is_allocator

is_allocator(vault_address: str, address: str) -> bool

Check if an address is an allocator for the vault.

get_supply_queue

get_supply_queue(vault_address: str) -> list[str]

Read the vault's supply queue (list of market IDs).

get_withdraw_queue

get_withdraw_queue(vault_address: str) -> list[str]

Read the vault's withdraw queue (list of market IDs).

get_vault_info

get_vault_info(vault_address: str) -> VaultInfo

Read complete vault information in multiple RPC calls (generation-aware).

The ERC-4626 block is shared; the generation-specific block reads the selectors that exist on that generation only (v1 fee/timelock revert on V2, V2 fee/adapter reads revert on v1).

get_position

get_position(
    vault_address: str, user: str
) -> VaultPosition

Read a user's position in the vault.

build_deposit_tx

build_deposit_tx(
    vault_address: str, assets: int, receiver: str
) -> dict

Build an unsigned ERC-4626 deposit(uint256,address) transaction.

Parameters:

Name Type Description Default
vault_address str

The MetaMorpho vault address.

required
assets int

Amount of underlying assets to deposit (raw units).

required
receiver str

Address to receive vault shares.

required

Returns:

Type Description
dict

Unsigned transaction dict with keys: to, from, data, value, gas_estimate.

build_redeem_tx

build_redeem_tx(
    vault_address: str,
    shares: int,
    receiver: str,
    owner: str,
) -> dict

Build an unsigned ERC-4626 redeem(uint256,address,address) transaction.

Parameters:

Name Type Description Default
vault_address str

The MetaMorpho vault address.

required
shares int

Number of shares to redeem (raw units).

required
receiver str

Address to receive underlying assets.

required
owner str

Address that owns the shares being redeemed.

required

Returns:

Type Description
dict

Unsigned transaction dict with keys: to, from, data, value, gas_estimate.

build_approve_tx

build_approve_tx(
    token_address: str,
    spender: str,
    amount: int,
    owner: str,
) -> dict

Build an ERC-20 approve transaction.

Parameters:

Name Type Description Default
token_address str

The ERC-20 token address.

required
spender str

The address to approve.

required
amount int

Amount to approve (raw units).

required
owner str

The address that owns the tokens (tx sender).

required

Returns:

Type Description
dict

Unsigned transaction dict.

MetaMorphoSDKError

Bases: Exception

Base exception for MetaMorpho SDK errors.

RPCError

Bases: MetaMorphoSDKError

Raised when an RPC call fails.

UnsupportedChainError

Bases: MetaMorphoSDKError

Raised when chain is not supported.

VaultInfo dataclass

VaultInfo(
    address: str,
    asset: str,
    total_assets: int,
    total_supply: int,
    share_price: int,
    decimals: int,
    curator: str,
    fee: int,
    timelock: int,
    vault_version: str = VAULT_VERSION_V1,
    management_fee: int = 0,
    liquidity_adapter: str | None = None,
    adapters: list[str] = list(),
    force_deallocate_penalty: int | None = None,
)

Information about a MetaMorpho vault.

VaultMarketConfig dataclass

VaultMarketConfig(
    market_id: str,
    cap: int,
    enabled: bool,
    removable_at: int,
)

Market configuration within a MetaMorpho vault (Phase 2).

VaultNotFoundError

Bases: MetaMorphoSDKError

Raised when vault contract does not exist or returns invalid data.

VaultPosition dataclass

VaultPosition(
    vault_address: str, user: str, shares: int, assets: int
)

User position in a MetaMorpho vault.

create_test_adapter

create_test_adapter(
    chain: str = "ethereum",
    wallet_address: str = "0x1234567890123456789012345678901234567890",
) -> MetaMorphoAdapter

Create a test adapter without gateway client (for unit tests).

For unit testing only. On-chain operations will raise RuntimeError.

Parameters:

Name Type Description Default
chain str

Chain name (default: ethereum)

'ethereum'
wallet_address str

Wallet address (default: test address)

'0x1234567890123456789012345678901234567890'

Returns:

Type Description
MetaMorphoAdapter

MetaMorphoAdapter configured for testing