Docs

Depositing & Withdrawing from Vaults

Morpho Vault V2 follows the standard ERC‑4626 interface for deposit and withdrawal operations. That means deposit(), withdraw(), mint(), and redeem() behave like familiar tokenized vault flows for new Earn integrations on Morpho.

This guide shows the recommended integration path for Morpho Vault deposits and withdrawals: the Morpho SDK (@morpho-org/morpho-sdk). It builds final, ready-to-send viem transactions and resolves all on-chain pre-requisites for you - ERC-20 approvals, Permit / Permit2 signatures, native-token wrapping, slippage protection, and Bundler3 routing - whether you're building a dApp frontend or a backend service.

Key Concepts: Assets vs. Shares

When interacting with ERC4626 vaults, you have two approaches for deposits and withdrawals: an asset-first approach or a shares-first approach. Understanding the difference is key to a robust integration.

ApproachDeposit FunctionWithdrawal FunctionDescription
Asset-Firstdeposit(assets, ...)withdraw(assets, ...)You specify the exact amount of the underlying token (e.g., USDC, WETH) you want to deposit or withdraw.
Shares-Firstmint(shares, ...)redeem(shares, ...)You specify the exact number of vault shares you want to mint or redeem.

Best Practice:

  • For deposits, deposit() is the most common and intuitive function.
  • For full withdrawals, redeem() is recommended. Redeeming all of a user's shares ensures their balance goes to zero and avoids leaving behind small, unusable amounts of "dust."
  • For partial withdrawals where a user needs a specific amount of the underlying asset, withdraw() is appropriate.

Prerequisites

Before you begin, you will need:

  • The address of the Morpho Vault you want to interact with. You can find active vaults using the Morpho API.
  • An account with a balance of the vault's underlying asset (e.g., WETH for a WETH vault).

Vault Safety: Inflation Attack Protection

All ERC4626-compliant vaults, including Morpho Vaults, require a dead deposit to protect against share inflation attacks. This protection must be verified before your first interaction with a vault.

Verification Check:

// Check that the vault has adequate protection
const deadAddress = "0x000000000000000000000000000000000000dEaD";
const deadShares = await vault.balanceOf(deadAddress);

if (deadShares < 1_000_000_000n) {
  throw new Error("Vault lacks required inflation protection");
}

Key Points:

  • The dead deposit must be at least 1e9 shares for assets with more than 9 decimals, or 1e12 shares otherwise (approximately $1 equivalent).
  • This check should be performed during vault integration/whitelisting, not on every transaction
  • Properly protected vaults make inflation attacks economically unfeasible
  • Morpho Vault V2 integrations should ensure this protection is established by curators

For a detailed explanation of how this protection works, see Vault Mechanics: Inflation Attack Protection.

Integrate with the Morpho SDK

For an end-to-end walkthrough of every vault action this SDK exposes (deposit, withdraw, redeem, force-withdraw) see the Vault subpage; the Morpho SDK page covers the getRequirements flow, the builder = signer invariant, and error classes.

Step 1: Install and set up the client

npm install @morpho-org/morpho-sdk@5.4.1 viem@^2.0.0
# or
pnpm add @morpho-org/morpho-sdk@5.4.1 viem@^2.0.0
# or
yarn add @morpho-org/morpho-sdk@5.4.1 viem@^2.0.0
import { createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { mainnet } from "viem/chains";
import { morphoViemExtension } from "@morpho-org/morpho-sdk";

const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);

const client = createWalletClient({
  account,
  chain: mainnet,
  transport: http(process.env.RPC_URL_MAINNET),
}).extend(
  morphoViemExtension({
    // Enables Permit / Permit2 in getRequirements() so users skip the extra approve tx.
    supportSignature: true,
  }),
);

const vault = client.morpho.vaultV2("0xVaultAddress...", mainnet.id);

Step 2: Deposit

import { parseUnits } from "viem";

const vaultData = await vault.getData(); // fresh on-chain state with accrued interest

const deposit = vault.deposit({
  amount: parseUnits("1.0", 18), // 1 underlying token
  userAddress: account.address,
  vaultData,
});

// 1. Resolve approvals / permits before sending the deposit
const requirements = await deposit.getRequirements();
const signatures = [];
for (const req of requirements) {
  if ("sign" in req) {
    // Permit / Permit2: capture the off-chain signature
    signatures.push(await req.sign(client, account.address));
  } else {
    // ERC-20 approval: send the approval transaction
    await client.sendTransaction(req);
  }
}

// 2. Build and send the deposit tx (same client that built it - "builder = signer").
//    buildTx takes the collected requirement signatures as an array.
const depositTx = deposit.buildTx(signatures);
const depositTxHash = await client.sendTransaction(depositTx);
console.log("Deposit successful:", depositTxHash);

Step 3: Withdraw / redeem

// Redeem all of a user's shares (recommended for full exits - leaves no dust)
const userShares = vaultData.toShares(parseUnits("1.0", 18));

const redeem = vault.redeem({
  shares: userShares,
  userAddress: account.address,
});

const redeemTxHash = await client.sendTransaction(redeem.buildTx());
console.log("Withdrawal successful:", redeemTxHash);

For partial withdrawals by exact asset amount, use vault.withdraw({ amount, userAddress }) instead of redeem.

Additional Considerations

Slippage for User Experience

Vault deposits convert assets into shares. Between quote and execution, the vault share price can move because interest accrues or vault state changes. Slippage tolerance lets the transaction revert if the user would receive materially fewer shares than expected.

The SDK computes the maxSharePrice guard from fresh vault data and your slippageTolerance, then routes the deposit through Bundler3 / GeneralAdapter1.

The SDK default is 0.03%; the maximum accepted value is 10%. A tighter value gives stronger price protection but can cause more reverts when vault state changes before inclusion.

Step 1: Set the deposit inputs

Choose the asset amount and maximum tolerated movement before building the SDK transaction. Slippage is expressed in WAD units: parseUnits("0.01", 18) means 1%.

import { parseUnits, type Address } from "viem";
import { mainnet } from "viem/chains";
import {
  isRequirementSignature,
  type RequirementSignature,
} from "@morpho-org/morpho-sdk";

// Assumes `client` is a viem wallet client extended with `morphoViemExtension()`
// and `account` is the connected signer.
const vaultAddress = "0xVaultAddress..." as Address;
const userAddress = account.address;
const amount = parseUnits("1.0", 18); // replace 18 with the underlying asset decimals
const slippageTolerance = parseUnits("0.01", 18); // 1% in WAD units

const vault = client.morpho.vaultV2(vaultAddress, mainnet.id);
const vaultData = await vault.getData(); // fresh on-chain state with accrued interest

Step 2: Build the protected deposit transaction

Pass vaultData and slippageTolerance to the deposit builder so the SDK can build the onchain share-price guard.

const deposit = vault.deposit({
  amount,
  userAddress,
  vaultData,
  slippageTolerance,
});

Step 3: Resolve approvals and permits

getRequirements() returns approval transactions plus an optional Permit / Permit2 signature. Resolve them before sending the final deposit.

const requirements = await deposit.getRequirements();
const requirementSignatures: RequirementSignature[] = [];

for (const requirement of requirements) {
  if (isRequirementSignature(requirement)) {
    requirementSignatures.push(await requirement.sign(client, userAddress));
  } else {
    await client.sendTransaction(requirement);
  }
}

Step 4: Send the protected deposit

Build the final transaction with the optional signature, then send it with the same client that built the transaction.

const depositTxHash = await client.sendTransaction(deposit.buildTx(requirementSignatures));
console.log("Deposit sent:", depositTxHash);

See also Vault Mechanics: Slippage Considerations for more context.