In Morpho Blue, a token pair is a display label, while a market ID is the bytes32 hash of five parameters: loan token, collateral token, oracle, interest-rate model and liquidation loan-to-value. Markets with the same token addresses can have different IDs. A complete identity record also needs the chain and Morpho contract address, which are outside that hash.
Read the five parameters behind the pair
A screen can show two markets as “LOAN | COLLATERAL” even when their lending rules differ. Adding an LLTV percentage narrows the description but still leaves the oracle and interest-rate model unidentified. Morpho's documented app naming convention displays loan asset, collateral asset and LLTV; the market page also shows the oracle and IRM addresses. Morpho Blue market identification.
This article follows Blue's variable-rate isolated markets, using the official reference implementation at commit 9cf6d5b844625. Morpho Vaults V1/V2 add separate allocation and share-accounting layers; fixed-rate Midnight has a different design. Establish which product the record describes before applying this worksheet.
The five fields have a fixed order in the pinned MarketParams definition:
| Field | What must match | What a pair label hides |
|---|---|---|
| loanToken | Exact token address | Which asset suppliers lend and borrowers owe |
| collateralToken | Exact token address | Which asset backs borrowing in this market |
| oracle | Exact oracle address | Which contract prices collateral in loan-asset terms |
| irm | Exact interest-rate-model address | Which model supplies the borrowing rate |
| lltv | Exact integer scaled by 10^18 | The market's liquidation loan-to-value parameter |
Symbols are useful for reading, but the hash uses addresses. Two tokens can present the same symbol while occupying different addresses. Token roles also matter: reversing loanToken and collateralToken changes the input even if an interface continues to show the same two names.
Keep LLTV's unit explicit. An 86% LLTV is the integer 860,000,000,000,000,000, because 0.86 is scaled by 10^18. Passing the integer 86 encodes a different value. Morpho documents the scale in its LLTV contract reference. The broader LTV versus liquidation-threshold guide explains an Aave-style model with separate borrowing and liquidation limits. Blue's LLTV field should be interpreted within Blue's own health calculation.
The hash identifies parameters within a deployment
The pinned MarketParamsLib hashes exactly five 32-byte words, totaling 160 bytes. Reproduce that input with ordinary ABI encoding of four addresses followed by one uint256, then apply Ethereum Keccak-256. Each address occupies a padded 32-byte word. Packed encoding of those same typed values occupies 112 bytes and produces the wrong digest for this purpose.
Neither chain ID nor the Morpho contract address appears among those five words. Consequently, identical parameter bytes on two chains produce the same market ID. They still refer to separate state on those chains. This is a consequence of the hash definition, even when a particular pair of deployments has no such matching market.
Use a record key containing chain ID, Morpho contract address and full market ID. Add a block reference for a state observation, and a user address for a position. Morpho's API documentation likewise includes chain context in market selectors and asks integrations to specify chainId for marketById queries.
A market ID is also distinct from a per-market contract address. In the reference Blue implementation, one Morpho contract stores market state indexed by ID and positions indexed by ID and user. Creating a market adds entries to these mappings. The pinned Morpho contract shows that storage layout and creation path.
Check an illustrative identity worksheet
The following fixture is entirely hypothetical. Its addresses are patterns chosen for reproducibility, and LOAN and COLLATERAL are invented symbols. These calculations do not assert that any address contains a token, oracle, IRM or Morpho deployment.
Define address L as 0x followed by forty copies of digit 1. Define C with forty copies of digit 2, O with digit 3 and I with digit 4. O2 uses forty copies of digit 5; I2 uses digit 6. For the deployment check, M uses forty copies of a and M2 uses forty copies of b. Every address contains exactly forty hexadecimal digits after its prefix.
Baseline A has loanToken L, collateralToken C, oracle O, irm I and lltv 860000000000000000. The hypothetical observation belongs to chain ID 1 and a chosen Morpho contract M. Compute each row separately, keeping unspecified fields equal to A.
| Record | Change from A | Computed ID, shortened for display | Same deployed market as A? |
|---|---|---|---|
| A | Baseline | 0x3a5d3559…500162b7 | Baseline record |
| B | LLTV becomes 770000000000000000 | 0x1e3af672…fa0c794d | No: different parameters |
| C | Oracle becomes O2 | 0x5b896547…cc957e57 | No: different parameters |
| D | IRM becomes I2 | 0x542b5698…5803f93a | No: different parameters |
| E | Chain becomes 8453; five fields unchanged | 0x3a5d3559…500162b7 | No: different chain |
| F | Morpho contract becomes M2; five fields unchanged | 0x3a5d3559…500162b7 | No: different contract |
For a complete reproducible output, A's digest has these four consecutive groups of sixteen hexadecimal characters: 3a5d3559bd3d60a4, 9f1ede51bb852107, 19693ac06666c8a3, 5e7dd687500162b7. Concatenate the groups without punctuation and prepend 0x to obtain the full bytes32 ID. The shortened table entries are reading aids; a real equality check must compare every byte.
The fixture was checked using installed viem 2.57.0 for ABI encoding and Keccak-256. A second construction manually padded each input to a 32-byte word and matched the ABI bytes exactly. Both produced the A digest above. Packed encoding instead produced a digest beginning 0x5ccdf7fa, confirming that a correct hash function cannot repair an incorrect input encoding.
Rows A through D retain the same exact loan and collateral addresses. Even adding “86%” to the pair label would leave A, C and D indistinguishable. Rows E and F demonstrate the complementary failure: a matching ID can pass a hash-only comparison while the observation comes from another namespace.
These are candidate parameter records. An arbitrary digest does not prove that createMarket succeeded. The selected deployment must have the relevant IRM and LLTV enabled, and a corresponding market must actually exist. The calculation deliberately makes no assumption that fictional I2 is enabled anywhere.
Separate immutable identity from changing state
For a created market in this reference implementation, changing one of the five parameters describes a different market. However, the market's balances, utilization and rate can change without a new ID. Fee is a separate state field: the owner's setFee function can update it while retaining the same parameter tuple. These distinctions are visible in the contract reference.
An unchanged oracle address also does not mean an unchanged oracle price. An unchanged IRM address does not fix tomorrow's borrowing rate. Investigate the chosen dependencies and their behavior separately; the identity check records which contracts govern the relationship. Avoid assuming that every enabled IRM follows one particular curve. The borrow-rate versus supply-rate explanation continues that analysis after the market is identified.
Market identity must also precede balance conversion. Blue's positions contain internal supply and borrow shares, plus a collateral amount. They belong to a particular market. The market-mechanics documentation describes these separate records. Combining one market's position with another market's totals can produce plausible arithmetic for the wrong claim.
For exact conversions, the pinned SharesMathLib adds one virtual asset and 1,000,000 virtual shares. First multiply shares by (totalAssets + 1). Divide by (totalShares + 1,000,000), rounding down for toAssetsDown and up for toAssetsUp. Use totals from the correct supply or borrow side. The supply-shares versus assets guide develops that calculation; the Aave scaled-balance ledger follows a different accounting mechanism.
Validate a real record before joining its data
Start by fixing the chain, verified Morpho contract and observation block. Select the deployment through a trusted deployment reference, then use its actual interface. A familiar address pasted from another network is insufficient context.
Read idToMarketParams for the candidate ID from that contract. Recover all five raw values and compare them with the intended market. Then recompute the ID using the fixed field order and 160-byte encoding. A mismatch calls for checking address roles, padding and LLTV scaling before comparing rates or balances. Hashing the displayed symbols cannot validate the record.
Check existence separately. In the pinned creation path, a created market has nonzero lastUpdate; a new successful creation stores its parameters and emits CreateMarket. Read market(id) at the selected block, and corroborate creation with a log from the selected Morpho contract when historical provenance is needed. An existing empty market can have zero supplied assets, so zero liquidity alone cannot settle the existence question.
Once identity matches, bind the remaining observations to it:
| Observation | Record alongside the market key |
|---|---|
| Token metadata | Exact address, decimals and display symbol |
| Dependencies | Oracle and IRM addresses, plus inspected configuration |
| Market state | Supply and borrow totals, fee and lastUpdate |
| User position | User address, supply shares, borrow shares and collateral |
| Time basis | Block reference and whether pending interest is included |
The Blue contract documentation warns that market(id) returns stored asset totals without accruing pending interest. A matching identity therefore does not automatically make a stored snapshot comparable to an interface's later accrued estimate. Resolve the time basis before calling a difference an accounting error.
For a data join, require the complete deployment key and full ID to match. For a proposed transaction, also verify the five parameters supplied to the call. If the pair matches but the parameters differ, retain separate records. If the key matches but the balances differ, inspect the block and accrual basis next. That order preserves the evidence needed to explain both which market was selected and which state was observed.
