Morpho Markets Adapter
What It Tracks
- Supply positions (lending)
- Borrow positions (debt)
Design Reasoning
WhyassetSelectors.marketId?
Morpho Blue has many markets, each identified by a marketId. The morphoBlueAddress identifies the protocol, but you need marketId to know WHICH market to track.
Morpho uses a shares-based accounting system:
- Users hold "shares" not raw assets
- Share value appreciates with interest
- The pricer converts shares → assets → USD
In the output, we create separate asset keys:
morpho:{marketId}-supplyfor lendersmorpho:{marketId}-borrowfor borrowers
Manifest
export const manifest: Manifest = {
name: 'morpho-markets',
version: '0.0.1',
chainArch: 'evm',
trackables: {
morphoblue: {
kind: 'position',
quantityType: 'token_based',
params: {
morphoBlueAddress: evmAddress('The morpho blue address'),
},
assetSelectors: {
marketId: stringField('The market id to track'),
},
requiredPricer: morphomarketsFeed,
},
},
};
Event Handling
// Events tracked: Supply, Withdraw, Borrow, Repay
async function handleSupply(log, emitFns, instance, marketId, redis) {
const { id, onBehalf, assets, shares } = morphov1Abi.events.Supply.decode(log);
// Only process events for our configured market
if (id.toLowerCase() !== marketId) return;
// Update market index in Redis (for pricing)
const supplyIndex = deriveIndexFromEvent(assets, shares);
await updateMarketData(redis, marketId, { supplyIndex });
// Emit the position change
await emitFns.position.balanceDelta({
user: onBehalf.toLowerCase(),
asset: { type: 'custom', prefix: 'morpho', key: `${marketId}-supply` },
amount: BigInt(shares), // Track shares, not assets
activity: 'hold',
trackableInstance: instance,
meta: { marketId, shares: shares.toString(), positionSide: 'supply' },
});
}
async function handleBorrow(log, emitFns, instance, marketId, redis) {
const { id, onBehalf, assets, shares } = morphov1Abi.events.Borrow.decode(log);
if (id.toLowerCase() !== marketId) return;
const borrowIndex = deriveIndexFromEvent(assets, shares);
await updateMarketData(redis, marketId, { borrowIndex });
await emitFns.position.balanceDelta({
user: onBehalf.toLowerCase(),
asset: { type: 'custom', prefix: 'morpho', key: `${marketId}-borrow` },
amount: BigInt(shares),
activity: 'hold',
trackableInstance: instance,
meta: { marketId, shares: shares.toString(), positionSide: 'borrow' },
});
}
Example Config
{
"adapterConfig": {
"adapterId": "morpho-markets",
"config": {
"morphoblue": [
{
"params": {
"morphoBlueAddress": "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb"
},
"assetSelectors": {
"marketId": "0xd0d22868a9460dd837f020a1ba300b81f26520602cc61f2b515cf6413b15ddac"
},
"pricing": {
"kind": "morphomarkets",
"underlyingAsset": {
"kind": "pegged",
"usdPegValue": 1
}
}
}
]
}
}
}
Config Fields
| Field | Description |
|---|---|
params.morphoBlueAddress | The Morpho Blue protocol contract |
assetSelectors.marketId | The specific market to track (bytes32) |
pricing.underlyingAsset | How to price the underlying loan asset |
Key Patterns
- Filter by marketId: Only process events for the configured market
- Track shares, not assets: Shares are the accounting unit; pricer handles conversion
- Separate supply/borrow: Different asset keys for different position types
- Update indices in Redis: Pricing needs up-to-date market indices