GraphQL schema for historical activity, premium accrual, NAV reconciliation. Used by the Activity tab in the reference frontend.
type Vault @entity {
id: ID! # vault address (lowercase)
asset: Bytes!
underlyingVault: Bytes! # the wrapped ERC4626
seniorToken: Bytes!
juniorToken: Bytes!
protectionPremiumBps: BigInt!
seniorNav: BigInt!
juniorNav: BigInt!
seniorTotalSupply: BigInt!
juniorTotalSupply: BigInt!
totalVaultShares: BigInt!
lastPremiumAccrual: BigInt!
depositsPaused: Boolean!
...
}
type Position @entity {
id: ID! # vaultAddress-tranche-account
vault: Vault!
tranche: Tranche! # SENIOR | JUNIOR
account: Bytes!
shares: BigInt! # current lcSEN/lcJUN balance
cumulativeDeposited: BigInt! # lifetime asset deposit
cumulativeWithdrawn: BigInt! # lifetime asset withdrawal
noticeShares: BigInt! # > 0 ⇒ withdrawal notice on file
noticeExecutableAt: BigInt # timestamp the notice unblocks
}
type Snapshot @entity {
id: ID! # vaultAddress-timestamp
vault: Vault!
timestamp: BigInt!
seniorNav: BigInt!
juniorNav: BigInt!
seniorSharePrice: BigInt! # 1e18-scaled
juniorSharePrice: BigInt! # 1e18-scaled
totalAssets: BigInt!
}Position is keyed by (vault, tranche, account), one row per holder per side. Each user has at most one position per tranche per vault.
The frontend's Activity tab queries a single Activity union-style entity that subsumes every event type. Each row carries:
type Activity @entity {
id: ID! # txHash-logIndex
vault: Vault!
kind: ActivityKind!
timestamp: BigInt!
account: Bytes
receiver: Bytes
txHash: Bytes!
assets: BigInt # primary amount, varies by kind
shares: BigInt # secondary amount for share-denominated events
extra: ActivityExtra! # kind-specific JSON-shaped fields
}
enum ActivityKind {
SeniorDeposit
JuniorDeposit
SeniorWithdrawal
JuniorWithdrawal
SeniorWithdrawalNoticeRequested
SeniorWithdrawalNoticeCancelled
JuniorWithdrawalNoticeRequested
JuniorWithdrawalNoticeCancelled
PooledNavSynced
ProtectionPremiumAccrued
ProtectionPremiumRateScheduled
ProtectionPremiumRateActivated
DepositsPauseToggled
SeniorDepositsPauseToggled
}extra carries kind-specific fields like delta (for NAV sync), fromSeniorNav / toJuniorNav (for premium accrual), previousRateBps / newRateBps / effectiveAt (for rate scheduling), paused (for pause toggles), and executableAt (for notice requests). The frontend's notesCell and amountCell formatters branch on kind to pull the right field.
query RecentActivity($vault: ID!, $first: Int!) {
activities(
where: { vault: $vault }
orderBy: timestamp
orderDirection: desc
first: $first
) {
id
kind
timestamp
account
receiver
txHash
assets
shares
extra
}
}query UserPositions($user: Bytes!) {
positions(where: { account: $user, shares_gt: 0 }) {
vault {
id
asset
protectionPremiumBps
seniorSharePrice: derivedSeniorSharePrice # see schema notes
juniorSharePrice: derivedJuniorSharePrice
}
tranche
shares
noticeShares
noticeExecutableAt
}
}query NavHistory($vault: ID!, $sinceTs: BigInt!) {
snapshots(
where: { vault: $vault, timestamp_gte: $sinceTs }
orderBy: timestamp
orderDirection: asc
first: 1000
) {
timestamp
seniorSharePrice
juniorSharePrice
seniorNav
juniorNav
}
}The frontend's useTrancheSnapshots hook uses the share-price column to derive realised APY from two consecutive snapshots: (price_t / price_t-1)^(1y / dt) − 1.
For indexing Morpho VaultV2 instances directly, see Morpho's official tooling at docs.morpho.org/tools.