Docs

Morpho SDK

Introduction

The Morpho SDK (@morpho-org/morpho-sdk) is the default and recommended SDK for building applications on top of Morpho.

It is the abstraction layer that simplifies the Morpho protocol: it produces ready-to-send viem transactions for VaultV2 and Variable Rate - Blue Market (Morpho Blue) on every EVM-compatible chain Morpho is deployed on - plus Fixed Rate - Midnight Market through the same flow - while taking care of the Bundler3 / GeneralAdapter1 plumbing, slippage protection, ERC-20 approvals, Permit / Permit2 signatures, Morpho authorizations, native-token wrapping, Blue-to-Blue refinance, and shared-liquidity reallocations for you.

The SDK also supports refinance: atomically moving a borrower's collateral and debt from one Morpho Blue market to another market that shares the same loan token and collateral token, optionally using shared liquidity through the PublicAllocator.

If you are integrating Morpho into a wallet, dApp, partner app, agent, or backend service, this is the SDK you want.

Choose your product

Each Morpho product surface has its own subpage. Find the product you are shipping and go straight there - every subpage reuses the same client Setup and getRequirements / buildTx flow documented on this page, so adding a second product later is only a new entity factory call.

You are buildingMorpho productGo to
An earn product: users deposit an asset and accrue yieldVaultV2 (curated vaults)Vault V2
Variable-rate borrowing against collateral, with no fixed termMorpho Blue marketsVariable Rate - Blue Market
Fixed-rate, fixed-term lending or borrowing on an onchain orderbookMidnightFixed Rate - Midnight Market

Actions overview

Every action the SDK builds, per entity - with the route each transaction takes and why:

EntityActionRouteWhy
VaultV2depositBundler3 via GeneralAdapter1Enforces maxSharePrice against ERC-4626 share-price inflation and supports native-token wrapping.
withdrawDirect vault callWithdraws an exact asset amount without bundler overhead.
redeemDirect vault callRedeems an exact share amount without bundler overhead.
forceWithdrawVault multicallRuns forceDeallocate calls before one withdraw in a single vault transaction.
forceRedeemVault multicallRuns forceDeallocate calls before one redeem in a single vault transaction.
VaultV1depositBundler3 via GeneralAdapter1Enforces maxSharePrice against ERC-4626 share-price inflation and supports native-token wrapping.
withdrawDirect vault callWithdraws an exact asset amount without bundler overhead.
redeemDirect vault callRedeems an exact share amount without bundler overhead.
migrateToV2Bundler3 via GeneralAdapter1Atomically redeems V1 shares and deposits the assets into V2 with share-price bounds on both legs.
Blue MarketsupplyCollateralBundler3 via GeneralAdapter1Pulls collateral into Morpho and supports native wrapping when collateral is wNative.
supplyBundler3 via GeneralAdapter1Calls morphoSupply with a forward-accrued maxSharePrice guard and optional native wrapping.
withdrawBundler3 via GeneralAdapter1Calls morphoWithdraw with minSharePrice, Morpho authorization, and optional shared-liquidity reallocations.
borrowBundler3 via GeneralAdapter1Calls morphoBorrow with minSharePrice, LLTV-buffer validation, Morpho authorization, and optional reallocations.
repayBundler3 via GeneralAdapter1Calls morphoRepay with a forward-accrued maxSharePrice; supports amount, shares, and native funding.
withdrawCollateralDirect Morpho callCalls withdrawCollateral directly after validating the resulting position health.
repayWithdrawCollateralBundler3 via GeneralAdapter1Repays before withdrawing collateral in one bundle, then validates the combined post-state.
supplyCollateralBorrowBundler3 via GeneralAdapter1Supplies collateral then borrows atomically with LLTV-buffer validation and optional reallocations.
refinanceBundler3 via GeneralAdapter1Migrates collateral and debt between compatible Blue markets atomically, with optional target reallocations.
MidnighttakeLendMidnightBundles bundleTakes borrow-side offers for a lender with a deadline, token approval, and MidnightBundles authorization.
takeBorrowMidnightBundles bundleTakes lend-side offers for a borrower with a deadline and MidnightBundles authorization.
supplyCollateralTakeBorrowMidnightBundles bundleSupplies collateral and takes lend-side offers atomically with a deadline.
supplyCollateralDirect Midnight callSupplies configured collateral directly to the selected Midnight market.
makeLendMidnight mempool submissionValidates and submits lend-side maker offers after reserve and ratifier requirements are satisfied.
makeBorrowMidnight mempool submissionValidates and submits borrow-side maker offers after ratifier requirements are satisfied.
supplyCollateralMakeBorrowMidnight mempool submissionSupplies collateral as a requirement, then submits validated borrow-side maker offers.
redeemDirect Midnight callRedeems accrued fixed-rate credit directly to the selected receiver.
repayWithdrawCollateralMidnightBundles bundleRepays debt, withdraws collateral, or does both through one deadline-bound bundle.
cancelOfferDirect Midnight callFully consumes a maker offer group directly on Midnight.

This page documents the shared client setup and the getRequirements / buildTx flow; the VaultV2, Blue Market, and fixed-rate Midnight surfaces are each documented on their own subpage. VaultV1 (MetaMorpho) mirrors the VaultV2 vault surface and adds a V1→V2 migration.

Installation

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
# or
npm install @morpho-org/morpho-sdk@5.4.1 viem@^2.0.0

viem is a peer dependency (^2.0.0) and must be installed alongside the SDK.

Setup

import "dotenv/config";
import { type Address, createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { mainnet } from "viem/chains";
import { morphoViemExtension } from "@morpho-org/morpho-sdk";

// Private-key account: this is the connected account the SDK enforces as the signer.
const account = privateKeyToAccount(process.env.PRIVATE_KEY as Address);

// This is the account the builders must be told about (see the invariant below).
const USER_ADDRESS = account.address;

// Extend a viem wallet client with the Morpho namespace. `client.morpho` exposes
// four stateless entity factories: `vaultV1`, `vaultV2`, `blue`, and `midnight`.
const client = createWalletClient({
  account,
  chain: mainnet,
  transport: http(process.env.RPC_URL),
}).extend(
  morphoViemExtension({
    // Collect EIP-712 permit / permit2 signatures instead of only on-chain ERC-20
    // approvals; defaults to `false` (classic approvals only).
    supportSignature: true,
    // Analytics metadata stamped onto every transaction the namespace builds.
    // `timestamp` is optional; `origin` is required when `metadata` is passed.
    // `origin` is hexadecimal calldata (at most 8 hex chars / 4 bytes, even
    // length, optional 0x prefix), NOT a clear-text name - an invalid origin
    // is dropped with only a console warning.
    metadata: { origin: "0xbeef01", timestamp: true },
    // Allow entity fetchers to use deployless multicall for on-chain reads.
    supportDeployless: true,
  }),
);

// Builder = signer invariant:
// The `userAddress` you pass to any builder MUST equal the account connected to THIS
// extended client, and the SAME client MUST sign and send the resulting transaction.
// Signature requirements enforce it when `sign(client, userAddress)` is called, via
// `validateUserAddress` - throwing `MissingClientPropertyError` when the client has
// no connected account, or `AddressMismatchError` when the connected account does
// not match `userAddress`. Transaction builders do not re-validate at build time.

Extend your viem client with morphoViemExtension() to add the client.morpho namespace. That namespace exposes four entity factories:

import { MarketParams } from "@morpho-org/morpho-sdk/entities";

// chainId is mandatory (validated against the viem client).
const vaultV2 = client.morpho.vaultV2(
  "0xVaultV2Address0000000000000000000000000000" as Address,
  mainnet.id,
);

// `blue()` derives and validates the market id from these params, so construct a
// `MarketParams` instance - a raw object literal has no id and throws MarketIdMismatchError.
const market = client.morpho.blue(
  new MarketParams({
    loanToken: "0xLoanToken00000000000000000000000000000000" as Address,
    collateralToken: "0xCollateralToken00000000000000000000000000" as Address,
    oracle: "0xOracle0000000000000000000000000000000000" as Address,
    irm: "0xIrm00000000000000000000000000000000000000" as Address,
    lltv: 945000000000000000n, // 94.5%
  }),
  mainnet.id,
);

client.morpho.midnight(chainId) returns the fixed-rate Midnight market surface bound to the same viem client; see the Midnight subpage.

Extension options

Pass these options to morphoViemExtension(...):

OptionDefaultEffect
supportSignaturefalseLets the SDK return EIP-712 Permit, Permit2, and Morpho authorization signature requirements instead of relying only on approval and authorization transactions.
supportDeploylessundefinedLets entity fetchers use deployless multicall reads; when unset, fetchers use their default behavior.
metadataundefinedAppends analytics metadata to every built transaction. origin must be a hexadecimal byte string of at most 8 hex characters (4 bytes), with even length and an optional 0x prefix; invalid origin text is dropped with a console warning.

The getRequirements flow

Every action that touches a user's tokens or positions returns the same shape:

  • buildTx(signatures?) - returns the final, deep-frozen viem Transaction ({ to, value, data, action }). Collected requirement signatures are passed as an array.
  • getRequirements() - returns the on-chain pre-requisites that must be satisfied first.

A requirement is one of:

  • An ERC-20 approval transaction the user must send first (approving the bundler - or Morpho - to pull tokens).
  • A Permit / Permit2 signature request - a signable Requirement: call requirement.sign(client, userAddress) to collect the EIP-712 RequirementSignature, then pass it back into buildTx([signature]) as an array. Enabled with morphoViemExtension({ supportSignature: true }).
  • A Morpho authorization - morpho.setAuthorization(generalAdapter1, true). Required once per user for borrow, supplyCollateralBorrow, repayWithdrawCollateral, loan-asset withdraw, and refinance. The SDK only returns it if it is missing. With supportSignature: true it comes back as a signable Requirement instead of a transaction, and the signed authorization is folded into the bundle as a setAuthorizationWithSig call - no standalone transaction needed.

The canonical end-to-end flow - the same dispatch loop works for every vault, market, and Midnight taker action. Midnight maker flows are the exception: they submit signed offers to the Midnight mempool instead of returning a viem Transaction you send yourself - see the Midnight subpage.

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

// `client` is the extended wallet client from Setup; viem's `publicActions`
// adds `waitForTransactionReceipt` / `getBlock` for the requirement flow.
const publicClient = client.extend(publicActions);

const vaultData = await vaultV2.getData();

const deposit = vaultV2.deposit({
  amount: parseUnits("1", 18), // the vault asset has 18 decimals here
  userAddress: USER_ADDRESS,
  vaultData,
});

// 1. Resolve the on-chain pre-requisites.
const requirements = await deposit.getRequirements();

// 2. Dispatch every requirement: sign the signable ones (off-chain), send the
//    rest as transactions (on-chain) and wait for inclusion.
const signatures: RequirementSignature[] = [];

for (const requirement of requirements) {
  if (isRequirementSignature(requirement)) {
    // Permit / Permit2 / Morpho authorization signature request. `sign` runs
    // the EIP-712 signing flow and enforces builder = signer: it throws
    // `AddressMismatchError` unless the client's connected account equals
    // `USER_ADDRESS` (or `MissingClientPropertyError` when none is connected).
    signatures.push(await requirement.sign(client, USER_ADDRESS));
  } else {
    // ERC-20 approval or Morpho `setAuthorization` transaction. It must be
    // mined before the final transaction can execute.
    const hash = await client.sendTransaction(requirement);
    await publicClient.waitForTransactionReceipt({ hash });
  }
}

// 3. Build the final transaction, passing the collected signatures.
const tx = deposit.buildTx(signatures);
// `tx` is a deep-frozen `{ to, value, data, action }`.

// 4. Send it with the SAME client that built it.
await client.sendTransaction(tx);

isRequirementSignature splits the off-chain signature requests from the on-chain transactions. When you need finer-grained dispatch - for example different wallet-prompt copy per requirement - the SDK also exports isRequirementApproval and isRequirementBlueAuthorization:

import {
  isRequirementApproval,
  isRequirementBlueAuthorization,
  isRequirementSignature,
} from "@morpho-org/morpho-sdk";

for (const requirement of requirements) {
  if (isRequirementApproval(requirement)) {
    // ERC-20 approval transaction - `requirement.action.args` is { spender, amount }.
  } else if (isRequirementBlueAuthorization(requirement)) {
    // `morpho.setAuthorization(generalAdapter1, true)` transaction.
  } else if (isRequirementSignature(requirement)) {
    // Signable requirement - `requirement.action.type` is "permit", "permit2",
    // or "authorization".
  }
}

Builder = signer

userAddress MUST equal the connected account on the viem client used to build the transaction, and the SAME client MUST sign and send it.

Transaction builders do not re-validate this at build time - you must keep userAddress aligned with the signing account yourself. The invariant is enforced when a signature requirement is signed: sign(client, userAddress) runs validateUserAddress, which throws MissingClientPropertyError (no connected account) or AddressMismatchError (account mismatch).

This invariant exists because some bundles - particularly repayWithdrawCollateral - mix explicit onBehalf = userAddress (repay) with implicit msg.sender (transfer-from + withdraw). Splitting the builder and the signer would atomically repay one user's debt while withdrawing another user's collateral.

This is a build-time guard against accidental mixed-account bundles for honest integrators, not a defense against a malicious builder. The signer remains responsible for reviewing what they sign. See BUNDLER3.md for the deeper Bundler3 / GeneralAdapter1 context.

Fetching state

Each entity exposes thin wrappers over the canonical viem fetchers (re-exported from @morpho-org/morpho-sdk/fetch), so you always pass fresh, accrued data into the builders:

EntityMethodReturns
vaultV2vault.getData(parameters?)AccrualVaultV2 - total assets, total supply, asset address, share/asset conversion, curated allocations, adapters used by forceWithdraw / forceRedeem.
bluemarket.getMarketData(parameters?)Market - total supply / borrow assets and shares, utilization, liquidity, oracle price, rate-at-target.
bluemarket.getPositionData(user, parameters?)AccrualPosition - borrowAssets, collateral, supplyShares, borrowShares, maxBorrowAssets, ltv, isHealthy, plus the parent Market.

These data objects are the canonical entity classes re-exported from @morpho-org/morpho-sdk/entities. Refer to that module for the full type surface, accrual math, and helpers.

Errors and invariants

The SDK uses dedicated error classes (no generic Errors) so you can branch on failure modes deterministically. A few highlights:

ErrorWhen it triggers
NegativeInputErrorA scalar input that must be non-negative is negative; exposes the invalid field and value.
NonPositiveInputErrorA scalar input that must be positive is zero or negative; exposes the invalid field and value.
NonPositiveAssetAmountErrorDeprecated alias of NonPositiveInputError for an invalid asset amount.
NonPositiveSharesAmountErrorDeprecated alias of NonPositiveInputError for an invalid share amount.
NonPositiveMaxSharePriceErrorDeprecated alias of NonPositiveInputError for an invalid maximum share price.
ZeroDepositAmountErrorDeprecated alias of NonPositiveInputError for an invalid total deposit amount.
NonPositiveBorrowAmountErrorDeprecated alias of NonPositiveInputError for an invalid borrow amount.
ZeroCollateralAmountErrorDeprecated alias of NonPositiveInputError for an invalid total collateral amount.
NonPositiveReallocationAmountErrorDeprecated alias of NonPositiveInputError for an invalid reallocation amount.
NonPositiveRepayAmountErrorDeprecated alias of NonPositiveInputError for an invalid repay amount.
NonPositiveRepayMaxSharePriceErrorDeprecated alias of NonPositiveInputError for an invalid repay maximum share price.
NonPositiveWithdrawCollateralAmountErrorDeprecated alias of NonPositiveInputError for an invalid collateral withdrawal amount.
ZeroSupplyAmountErrorDeprecated alias of NonPositiveInputError for an invalid total supply amount.
NonPositiveWithdrawAmountErrorDeprecated alias of NonPositiveInputError for an invalid loan-asset withdrawal amount.
NegativeSlippageToleranceErrorDeprecated alias of NegativeInputError for an invalid slippage tolerance.
NegativeNativeAmountErrorDeprecated alias of NegativeInputError for an invalid native amount.
NegativeReallocationFeeErrorDeprecated alias of NegativeInputError for an invalid reallocation fee.
NonPositiveMinBorrowSharePriceErrorDeprecated alias of NegativeInputError for an invalid borrow minimum share price.
NegativeSupplyAmountErrorDeprecated alias of NegativeInputError for an invalid supply amount.
NegativeSupplyMaxSharePriceErrorDeprecated alias of NegativeInputError for an invalid supply maximum share price.
NegativeWithdrawMinSharePriceErrorDeprecated alias of NegativeInputError for an invalid withdraw minimum share price.
NegativeMinSharePriceErrorDeprecated alias of NegativeInputError for an invalid vault minimum share price.
NegativeBorrowSharesErrorDeprecated alias of NegativeInputError for an invalid refinance borrow shares.
NegativeMaxRepaySharePriceErrorDeprecated alias of NegativeInputError for an invalid refinance maximum repay share price.
BundlerErrors.MissingSignatureA Bundler3 action that requires an offchain signature is encoded before that signature is attached.
BundlerErrors.UnexpectedActionA Bundler3 action is unsupported on the requested chain.
BundlerErrors.UnexpectedSignatureA Blue authorization signature targets an account other than the chain's GeneralAdapter1.
AmbiguousRequirementSignaturesErrorbuildTx receives more than one requirement signature of the same accepted kind.
UnexpectedRequirementSignatureErrorbuildTx receives a requirement signature kind that the operation does not consume.
AddressMismatchErrorThe connected viem account differs from the address required by a signing flow.
ChainIdMismatchErrorThe viem client chain differs from the chain expected by the entity or action.
CryptoUnavailableErrorA flow needs a runtime cryptography API that is unavailable.
MissingClientPropertyErrorThe viem client lacks a required property such as account.address.
ApprovalAmountLessThanSpendAmountErrorAn ERC-20 approval amount is smaller than the amount the action must spend.
UnsupportedErc20ApprovalSpenderErrorA requirement targets a spender outside the allowed chain registry slots: GeneralAdapter1, Permit2, Midnight, or MidnightBundles as selected by the flow.
UnsupportedMidnightAuthorizationTargetErrorA Midnight authorization targets neither MidnightBundles nor the chain's Ecrecover or Setter ratifier.
MissingAccrualPositionErrorAn action that requires accrued position data receives no position snapshot.
ExcessiveSlippageToleranceErrorA supplied slippage tolerance exceeds MAX_SLIPPAGE_TOLERANCE (10%).
EmptyDeallocationsErrorVaultV2 forceWithdraw or forceRedeem receives no deallocations.
DepositAmountMismatchErrorA deposit amount differs from the amount covered by its permit or Permit2 signature.
DepositAssetMismatchErrorA deposit asset differs from the asset covered by its permit or Permit2 signature.
DepositOwnerMismatchErrorA deposit owner differs from the owner covered by its permit or Permit2 signature.
DepositSpenderMismatchErrorA deposit spender differs from the spender covered by its permit or Permit2 signature.
Permit2ExpirationMissingErrorA Permit2 requirement signature omits args.expiration.
NativeAmountOnNonWNativeVaultErrorA vault deposit uses nativeAmount when the vault asset is not the chain's wrapped native token.
ChainWNativeMissingErrorA flow uses nativeAmount on a chain with no configured wrapped native token.
VaultAddressMismatchErrorA vault entity address differs from the address in the supplied vault data.
NativeAmountOnNonWNativeAssetErrorAn action uses nativeAmount when its target asset is not the chain's wrapped native token.
NativeAmountOnNonWNativeCollateralErrorDeprecated alias of NativeAmountOnNonWNativeAssetError for collateral supply.
BorrowExceedsSafeLtvErrorA Blue borrow exceeds the LLTV-buffered safe maximum for the position.
MissingMarketPriceErrorA Blue market has no oracle price, so position health cannot be validated.
MarketIdMismatchErrorSupplied market data or parameters resolve to a market id different from the expected id.
AccrualPositionUserMismatchErrorAn accrued position belongs to a user other than the account the action targets.
EmptyReallocationWithdrawalsErrorA PublicAllocator reallocation entry contains no source-market withdrawals.
ReallocationWithdrawalOnTargetMarketErrorA reallocation tries to withdraw from the same market it targets.
UnsortedReallocationWithdrawalsErrorA vault's reallocation withdrawals are not strictly sorted by market id.
TransferAmountNotEqualToAssetsErrorA Blue assets-mode repay has transferAmount different from amount + nativeAmount.
MutuallyExclusiveRepayAmountsErrorA Blue repay supplies both amount and shares modes.
WithdrawExceedsCollateralErrorA collateral withdrawal exceeds the position's available collateral.
WithdrawMakesPositionUnhealthyErrorA collateral withdrawal would leave the position above the LLTV-buffered safe maximum.
ShareDivideByZeroErrorA share conversion would divide by zero because the relevant market share total is zero.
RepayExceedsDebtErrorAn assets-mode Blue repay exceeds the borrower's outstanding debt.
InvalidSignatureErrorEIP-712 signature verification does not recover the expected signer.
RepaySharesExceedDebtErrorA shares-mode Blue repay supplies more borrow shares than the borrower owes.
MissingPublicAllocatorConfigErrorA vault selected for reallocation has no PublicAllocator configuration.
DisabledReallocationMarketErrorA reallocation uses a market that the selected vault has disabled.
InsufficientSharedLiquidityErrorComputed shared liquidity cannot cover the target market's absolute operation shortfall.
UnknownReallocationMarketErrorReallocation state does not contain the requested market.
UnknownReallocationVaultErrorReallocation state does not contain the requested vault.
UnknownReallocationVaultMarketConfigErrorReallocation state does not contain the requested vault-market configuration.
UnknownReallocationPositionErrorReallocation state does not contain the requested vault position on a market.
MidnightAmountExceedsMaxOfferCapErrorA Midnight offer or cancellation amount exceeds the onchain maximum offer-cap value.
EmptyMidnightTakeableOffersErrorA Midnight take flow receives no takeable offers.
MidnightOfferSideMismatchErrorA Midnight offer has the wrong maker side for the selected lend or borrow flow.
MidnightOfferMakerMismatchErrorA prepared maker offer belongs to an account other than the active maker.
MidnightOfferMarketChainMismatchErrorA maker offer targets a chain other than the selected Midnight entity chain.
MidnightOfferMarketAddressMismatchErrorA maker offer targets a Midnight deployment other than the selected chain deployment.
MidnightMarketAddressMismatchErrorHydrated Midnight market data targets a deployment other than the selected chain deployment.
MidnightOfferMarketLoanTokenMismatchErrorA make-lend offer uses a loan token different from the approved reserve token.
MidnightTakeableOfferMarketMismatchErrorA quoted takeable offer belongs to a market other than the requested market.
UnknownMidnightRatifierErrorA maker offer tree uses neither the chain's Ecrecover ratifier nor its Setter ratifier.
MissingMidnightOfferRootSignatureErrorAn Ecrecover maker flow builds its submission before an offer-root signature is attached.
MidnightOfferRootMismatchErrorAn attached Midnight offer-root signature references a root different from the prepared tree.
MidnightOfferRootOwnerMismatchErrorAn attached Midnight offer-root signature was produced by a different maker.
MidnightOfferRootRatifierMismatchErrorAn attached Midnight offer-root signature targets a different ratifier.
MidnightOfferRootOfferCountMismatchErrorAn attached Midnight offer-root signature covers a different number of offers.
UnpreparedMidnightOfferRootSignatureErrorA Midnight offer-root signature was not prepared and recorded by the current maker flow.
NoMidnightCreditToRedeemErrorA Midnight redeem flow finds no positive credit units to redeem.
MidnightRedeemExceedsCreditErrorA Midnight redemption requests more units than the accrued position credit.
InsufficientMidnightWithdrawableLiquidityErrorA Midnight redemption exceeds the market's currently withdrawable liquidity.
MutuallyExclusiveWithdrawAmountsErrorA Blue loan-asset withdraw supplies both assets and shares modes.
WithdrawExceedsSupplyErrorAn assets-mode Blue loan-asset withdraw exceeds the user's supplied assets.
WithdrawSharesExceedSupplyErrorA shares-mode Blue loan-asset withdraw exceeds the user's supply shares.
ReallocationWithdrawExceedsMarketSupplyErrorA withdraw reallocation is requested for more than the target market's total supplied assets.
VaultAssetMismatchErrorA VaultV1-to-VaultV2 migration uses source and target vaults with different assets.
BorrowAmountAndSharesExclusiveErrorA refinance supplies both borrowAssets and borrowShares.
RefinanceSameMarketErrorA refinance uses the same Blue market as source and target.
RefinanceTokenMismatchErrorA refinance source and target do not share both loan and collateral tokens.
RefinanceExceedsCollateralErrorA refinance moves more collateral than the source position owns.
RefinanceExceedsBorrowSharesErrorA shares-mode refinance repays more borrow shares than the source position owes.
RefinanceExceedsBorrowAssetsErrorAn assets-mode refinance repays more assets than the source position owes.
RefinanceSharesMissingBorrowAssetsErrorA shares-mode refinance action omits the positive borrowAssets overshoot required for the target borrow leg.

The full list lives in src/types/error.ts.

Key invariants

  • Builder = signer. The viem client used to build a transaction MUST be the one used to sign and send it.
  • Transaction objects are deep-frozen. They cannot be mutated after buildTx.
  • No any. Strict TypeScript across the entire surface, with discriminated unions for every action type.
  • Chain id is mandatory. Every entity is constructed against a specific chain id, validated against the viem client.
  • Bundler3 is the default route for any operation that touches a user's tokens or position; direct calls are reserved for surfaces with no attack surface (vault withdraw / redeem, morpho.withdrawCollateral).
  • Slippage protection is always on. maxSharePrice (deposits, loan-asset supply, repay) and minSharePrice (borrows, loan-asset withdraw) are computed from fresh on-chain state and your slippage tolerance; the LLTV buffer additionally protects collateral operations.
  • Refinance markets must be compatible. Source and target markets must be different and must share loanToken and collateralToken.
  • Refinance uses fresh source and target positions. Always pass fresh source and target positionData; the SDK validates source residual health and target aggregate health with the LLTV buffer.
  • Refinance shares mode overshoots and sweeps. Shares mode overshoots the target borrow with slippage, repays source shares, then sweeps residual loan tokens back into target debt or user.
  • Collateral-only refinance skips target health validation. It skips target health / oracle validation because it cannot worsen target debt health.

Resources