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.
Source: morpho-org/sdks · packages/morpho-sdk · Package: @morpho-org/morpho-sdk on npm
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 building | Morpho product | Go to |
|---|---|---|
| An earn product: users deposit an asset and accrue yield | VaultV2 (curated vaults) | Vault V2 |
| Variable-rate borrowing against collateral, with no fixed term | Morpho Blue markets | Variable Rate - Blue Market |
| Fixed-rate, fixed-term lending or borrowing on an onchain orderbook | Midnight | Fixed Rate - Midnight Market |
Actions overview
Every action the SDK builds, per entity - with the route each transaction takes and why:
| Entity | Action | Route | Why |
|---|---|---|---|
| VaultV2 | deposit | Bundler3 via GeneralAdapter1 | Enforces maxSharePrice against ERC-4626 share-price inflation and supports native-token wrapping. |
withdraw | Direct vault call | Withdraws an exact asset amount without bundler overhead. | |
redeem | Direct vault call | Redeems an exact share amount without bundler overhead. | |
forceWithdraw | Vault multicall | Runs forceDeallocate calls before one withdraw in a single vault transaction. | |
forceRedeem | Vault multicall | Runs forceDeallocate calls before one redeem in a single vault transaction. | |
| VaultV1 | deposit | Bundler3 via GeneralAdapter1 | Enforces maxSharePrice against ERC-4626 share-price inflation and supports native-token wrapping. |
withdraw | Direct vault call | Withdraws an exact asset amount without bundler overhead. | |
redeem | Direct vault call | Redeems an exact share amount without bundler overhead. | |
migrateToV2 | Bundler3 via GeneralAdapter1 | Atomically redeems V1 shares and deposits the assets into V2 with share-price bounds on both legs. | |
| Blue Market | supplyCollateral | Bundler3 via GeneralAdapter1 | Pulls collateral into Morpho and supports native wrapping when collateral is wNative. |
supply | Bundler3 via GeneralAdapter1 | Calls morphoSupply with a forward-accrued maxSharePrice guard and optional native wrapping. | |
withdraw | Bundler3 via GeneralAdapter1 | Calls morphoWithdraw with minSharePrice, Morpho authorization, and optional shared-liquidity reallocations. | |
borrow | Bundler3 via GeneralAdapter1 | Calls morphoBorrow with minSharePrice, LLTV-buffer validation, Morpho authorization, and optional reallocations. | |
repay | Bundler3 via GeneralAdapter1 | Calls morphoRepay with a forward-accrued maxSharePrice; supports amount, shares, and native funding. | |
withdrawCollateral | Direct Morpho call | Calls withdrawCollateral directly after validating the resulting position health. | |
repayWithdrawCollateral | Bundler3 via GeneralAdapter1 | Repays before withdrawing collateral in one bundle, then validates the combined post-state. | |
supplyCollateralBorrow | Bundler3 via GeneralAdapter1 | Supplies collateral then borrows atomically with LLTV-buffer validation and optional reallocations. | |
refinance | Bundler3 via GeneralAdapter1 | Migrates collateral and debt between compatible Blue markets atomically, with optional target reallocations. | |
| Midnight | takeLend | MidnightBundles bundle | Takes borrow-side offers for a lender with a deadline, token approval, and MidnightBundles authorization. |
takeBorrow | MidnightBundles bundle | Takes lend-side offers for a borrower with a deadline and MidnightBundles authorization. | |
supplyCollateralTakeBorrow | MidnightBundles bundle | Supplies collateral and takes lend-side offers atomically with a deadline. | |
supplyCollateral | Direct Midnight call | Supplies configured collateral directly to the selected Midnight market. | |
makeLend | Midnight mempool submission | Validates and submits lend-side maker offers after reserve and ratifier requirements are satisfied. | |
makeBorrow | Midnight mempool submission | Validates and submits borrow-side maker offers after ratifier requirements are satisfied. | |
supplyCollateralMakeBorrow | Midnight mempool submission | Supplies collateral as a requirement, then submits validated borrow-side maker offers. | |
redeem | Direct Midnight call | Redeems accrued fixed-rate credit directly to the selected receiver. | |
repayWithdrawCollateral | MidnightBundles bundle | Repays debt, withdraws collateral, or does both through one deadline-bound bundle. | |
cancelOffer | Direct Midnight call | Fully 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.0viem 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(...):
| Option | Default | Effect |
|---|---|---|
supportSignature | false | Lets the SDK return EIP-712 Permit, Permit2, and Morpho authorization signature requirements instead of relying only on approval and authorization transactions. |
supportDeployless | undefined | Lets entity fetchers use deployless multicall reads; when unset, fetchers use their default behavior. |
metadata | undefined | Appends 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. |
metadata.origin is hexadecimal, not a clear-text app name. It is appended verbatim to every transaction's calldata as an analytics tag, so it must be a hex string of at most 8 hex characters (4 bytes), with an even number of characters, with or without the 0x prefix - e.g. "0xbeef01". An invalid origin (such as "my-app") does not throw: the SDK logs a console warning and omits the tag from the calldata.
The getRequirements flow
Every action that touches a user's tokens or positions returns the same shape:
buildTx(signatures?)- returns the final, deep-frozenviemTransaction({ 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: callrequirement.sign(client, userAddress)to collect the EIP-712RequirementSignature, then pass it back intobuildTx([signature])as an array. Enabled withmorphoViemExtension({ supportSignature: true }). - A Morpho authorization -
morpho.setAuthorization(generalAdapter1, true). Required once per user forborrow,supplyCollateralBorrow,repayWithdrawCollateral, loan-assetwithdraw, andrefinance. The SDK only returns it if it is missing. WithsupportSignature: trueit comes back as a signableRequirementinstead of a transaction, and the signed authorization is folded into the bundle as asetAuthorizationWithSigcall - 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
userAddressMUST equal the connected account on theviemclient 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:
| Entity | Method | Returns |
|---|---|---|
vaultV2 | vault.getData(parameters?) | AccrualVaultV2 - total assets, total supply, asset address, share/asset conversion, curated allocations, adapters used by forceWithdraw / forceRedeem. |
blue | market.getMarketData(parameters?) | Market - total supply / borrow assets and shares, utilization, liquidity, oracle price, rate-at-target. |
blue | market.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:
| Error | When it triggers |
|---|---|
NegativeInputError | A scalar input that must be non-negative is negative; exposes the invalid field and value. |
NonPositiveInputError | A scalar input that must be positive is zero or negative; exposes the invalid field and value. |
NonPositiveAssetAmountError | Deprecated alias of NonPositiveInputError for an invalid asset amount. |
NonPositiveSharesAmountError | Deprecated alias of NonPositiveInputError for an invalid share amount. |
NonPositiveMaxSharePriceError | Deprecated alias of NonPositiveInputError for an invalid maximum share price. |
ZeroDepositAmountError | Deprecated alias of NonPositiveInputError for an invalid total deposit amount. |
NonPositiveBorrowAmountError | Deprecated alias of NonPositiveInputError for an invalid borrow amount. |
ZeroCollateralAmountError | Deprecated alias of NonPositiveInputError for an invalid total collateral amount. |
NonPositiveReallocationAmountError | Deprecated alias of NonPositiveInputError for an invalid reallocation amount. |
NonPositiveRepayAmountError | Deprecated alias of NonPositiveInputError for an invalid repay amount. |
NonPositiveRepayMaxSharePriceError | Deprecated alias of NonPositiveInputError for an invalid repay maximum share price. |
NonPositiveWithdrawCollateralAmountError | Deprecated alias of NonPositiveInputError for an invalid collateral withdrawal amount. |
ZeroSupplyAmountError | Deprecated alias of NonPositiveInputError for an invalid total supply amount. |
NonPositiveWithdrawAmountError | Deprecated alias of NonPositiveInputError for an invalid loan-asset withdrawal amount. |
NegativeSlippageToleranceError | Deprecated alias of NegativeInputError for an invalid slippage tolerance. |
NegativeNativeAmountError | Deprecated alias of NegativeInputError for an invalid native amount. |
NegativeReallocationFeeError | Deprecated alias of NegativeInputError for an invalid reallocation fee. |
NonPositiveMinBorrowSharePriceError | Deprecated alias of NegativeInputError for an invalid borrow minimum share price. |
NegativeSupplyAmountError | Deprecated alias of NegativeInputError for an invalid supply amount. |
NegativeSupplyMaxSharePriceError | Deprecated alias of NegativeInputError for an invalid supply maximum share price. |
NegativeWithdrawMinSharePriceError | Deprecated alias of NegativeInputError for an invalid withdraw minimum share price. |
NegativeMinSharePriceError | Deprecated alias of NegativeInputError for an invalid vault minimum share price. |
NegativeBorrowSharesError | Deprecated alias of NegativeInputError for an invalid refinance borrow shares. |
NegativeMaxRepaySharePriceError | Deprecated alias of NegativeInputError for an invalid refinance maximum repay share price. |
BundlerErrors.MissingSignature | A Bundler3 action that requires an offchain signature is encoded before that signature is attached. |
BundlerErrors.UnexpectedAction | A Bundler3 action is unsupported on the requested chain. |
BundlerErrors.UnexpectedSignature | A Blue authorization signature targets an account other than the chain's GeneralAdapter1. |
AmbiguousRequirementSignaturesError | buildTx receives more than one requirement signature of the same accepted kind. |
UnexpectedRequirementSignatureError | buildTx receives a requirement signature kind that the operation does not consume. |
AddressMismatchError | The connected viem account differs from the address required by a signing flow. |
ChainIdMismatchError | The viem client chain differs from the chain expected by the entity or action. |
CryptoUnavailableError | A flow needs a runtime cryptography API that is unavailable. |
MissingClientPropertyError | The viem client lacks a required property such as account.address. |
ApprovalAmountLessThanSpendAmountError | An ERC-20 approval amount is smaller than the amount the action must spend. |
UnsupportedErc20ApprovalSpenderError | A requirement targets a spender outside the allowed chain registry slots: GeneralAdapter1, Permit2, Midnight, or MidnightBundles as selected by the flow. |
UnsupportedMidnightAuthorizationTargetError | A Midnight authorization targets neither MidnightBundles nor the chain's Ecrecover or Setter ratifier. |
MissingAccrualPositionError | An action that requires accrued position data receives no position snapshot. |
ExcessiveSlippageToleranceError | A supplied slippage tolerance exceeds MAX_SLIPPAGE_TOLERANCE (10%). |
EmptyDeallocationsError | VaultV2 forceWithdraw or forceRedeem receives no deallocations. |
DepositAmountMismatchError | A deposit amount differs from the amount covered by its permit or Permit2 signature. |
DepositAssetMismatchError | A deposit asset differs from the asset covered by its permit or Permit2 signature. |
DepositOwnerMismatchError | A deposit owner differs from the owner covered by its permit or Permit2 signature. |
DepositSpenderMismatchError | A deposit spender differs from the spender covered by its permit or Permit2 signature. |
Permit2ExpirationMissingError | A Permit2 requirement signature omits args.expiration. |
NativeAmountOnNonWNativeVaultError | A vault deposit uses nativeAmount when the vault asset is not the chain's wrapped native token. |
ChainWNativeMissingError | A flow uses nativeAmount on a chain with no configured wrapped native token. |
VaultAddressMismatchError | A vault entity address differs from the address in the supplied vault data. |
NativeAmountOnNonWNativeAssetError | An action uses nativeAmount when its target asset is not the chain's wrapped native token. |
NativeAmountOnNonWNativeCollateralError | Deprecated alias of NativeAmountOnNonWNativeAssetError for collateral supply. |
BorrowExceedsSafeLtvError | A Blue borrow exceeds the LLTV-buffered safe maximum for the position. |
MissingMarketPriceError | A Blue market has no oracle price, so position health cannot be validated. |
MarketIdMismatchError | Supplied market data or parameters resolve to a market id different from the expected id. |
AccrualPositionUserMismatchError | An accrued position belongs to a user other than the account the action targets. |
EmptyReallocationWithdrawalsError | A PublicAllocator reallocation entry contains no source-market withdrawals. |
ReallocationWithdrawalOnTargetMarketError | A reallocation tries to withdraw from the same market it targets. |
UnsortedReallocationWithdrawalsError | A vault's reallocation withdrawals are not strictly sorted by market id. |
TransferAmountNotEqualToAssetsError | A Blue assets-mode repay has transferAmount different from amount + nativeAmount. |
MutuallyExclusiveRepayAmountsError | A Blue repay supplies both amount and shares modes. |
WithdrawExceedsCollateralError | A collateral withdrawal exceeds the position's available collateral. |
WithdrawMakesPositionUnhealthyError | A collateral withdrawal would leave the position above the LLTV-buffered safe maximum. |
ShareDivideByZeroError | A share conversion would divide by zero because the relevant market share total is zero. |
RepayExceedsDebtError | An assets-mode Blue repay exceeds the borrower's outstanding debt. |
InvalidSignatureError | EIP-712 signature verification does not recover the expected signer. |
RepaySharesExceedDebtError | A shares-mode Blue repay supplies more borrow shares than the borrower owes. |
MissingPublicAllocatorConfigError | A vault selected for reallocation has no PublicAllocator configuration. |
DisabledReallocationMarketError | A reallocation uses a market that the selected vault has disabled. |
InsufficientSharedLiquidityError | Computed shared liquidity cannot cover the target market's absolute operation shortfall. |
UnknownReallocationMarketError | Reallocation state does not contain the requested market. |
UnknownReallocationVaultError | Reallocation state does not contain the requested vault. |
UnknownReallocationVaultMarketConfigError | Reallocation state does not contain the requested vault-market configuration. |
UnknownReallocationPositionError | Reallocation state does not contain the requested vault position on a market. |
MidnightAmountExceedsMaxOfferCapError | A Midnight offer or cancellation amount exceeds the onchain maximum offer-cap value. |
EmptyMidnightTakeableOffersError | A Midnight take flow receives no takeable offers. |
MidnightOfferSideMismatchError | A Midnight offer has the wrong maker side for the selected lend or borrow flow. |
MidnightOfferMakerMismatchError | A prepared maker offer belongs to an account other than the active maker. |
MidnightOfferMarketChainMismatchError | A maker offer targets a chain other than the selected Midnight entity chain. |
MidnightOfferMarketAddressMismatchError | A maker offer targets a Midnight deployment other than the selected chain deployment. |
MidnightMarketAddressMismatchError | Hydrated Midnight market data targets a deployment other than the selected chain deployment. |
MidnightOfferMarketLoanTokenMismatchError | A make-lend offer uses a loan token different from the approved reserve token. |
MidnightTakeableOfferMarketMismatchError | A quoted takeable offer belongs to a market other than the requested market. |
UnknownMidnightRatifierError | A maker offer tree uses neither the chain's Ecrecover ratifier nor its Setter ratifier. |
MissingMidnightOfferRootSignatureError | An Ecrecover maker flow builds its submission before an offer-root signature is attached. |
MidnightOfferRootMismatchError | An attached Midnight offer-root signature references a root different from the prepared tree. |
MidnightOfferRootOwnerMismatchError | An attached Midnight offer-root signature was produced by a different maker. |
MidnightOfferRootRatifierMismatchError | An attached Midnight offer-root signature targets a different ratifier. |
MidnightOfferRootOfferCountMismatchError | An attached Midnight offer-root signature covers a different number of offers. |
UnpreparedMidnightOfferRootSignatureError | A Midnight offer-root signature was not prepared and recorded by the current maker flow. |
NoMidnightCreditToRedeemError | A Midnight redeem flow finds no positive credit units to redeem. |
MidnightRedeemExceedsCreditError | A Midnight redemption requests more units than the accrued position credit. |
InsufficientMidnightWithdrawableLiquidityError | A Midnight redemption exceeds the market's currently withdrawable liquidity. |
MutuallyExclusiveWithdrawAmountsError | A Blue loan-asset withdraw supplies both assets and shares modes. |
WithdrawExceedsSupplyError | An assets-mode Blue loan-asset withdraw exceeds the user's supplied assets. |
WithdrawSharesExceedSupplyError | A shares-mode Blue loan-asset withdraw exceeds the user's supply shares. |
ReallocationWithdrawExceedsMarketSupplyError | A withdraw reallocation is requested for more than the target market's total supplied assets. |
VaultAssetMismatchError | A VaultV1-to-VaultV2 migration uses source and target vaults with different assets. |
BorrowAmountAndSharesExclusiveError | A refinance supplies both borrowAssets and borrowShares. |
RefinanceSameMarketError | A refinance uses the same Blue market as source and target. |
RefinanceTokenMismatchError | A refinance source and target do not share both loan and collateral tokens. |
RefinanceExceedsCollateralError | A refinance moves more collateral than the source position owns. |
RefinanceExceedsBorrowSharesError | A shares-mode refinance repays more borrow shares than the source position owes. |
RefinanceExceedsBorrowAssetsError | An assets-mode refinance repays more assets than the source position owes. |
RefinanceSharesMissingBorrowAssetsError | A 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
viemclient used to build a transaction MUST be the one used to sign and send it. Transactionobjects are deep-frozen. They cannot be mutated afterbuildTx.- 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) andminSharePrice(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
loanTokenandcollateralToken. - 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
- Repository: morpho-org/sdks · packages/morpho-sdk
- Package:
@morpho-org/morpho-sdkon npm - Architecture:
ARCHITECTURE.md - Bundler3 / GeneralAdapter1 deep-dive:
BUNDLER3.md - Examples:
examples/example.ts - Contributing:
CONTRIBUTING.md - Security:
SECURITY.md - Lower-level primitives:
@morpho-org/morpho-sdk/entities·@morpho-org/morpho-sdk/fetch