The canonical reference for this lifecycle (with a read-only Base wallet checker) is tech.mor.org/session.html. The page below is the curated repo-side summary; if it ever disagrees with
tech.mor.org, that page wins.End-to-end flow
The three places your MOR can be on chain
The Morpheus consumer node (proxy-router) does not custody tokens — it just calls the same Inference Contract functions you could call directly fromcast or MetaMask. So your consumer MOR is always in exactly one of three places:
1. Your wallet
Standard ERC-20
balanceOf(you) on the MOR token. Anything the contract safeTransfers to you during closeSession or withdrawUserStakes lands here.2. Active session escrow
openSession does transferFrom(you, InferenceContract, amount). While closedAt == 0, your stake lives inside the session record — not in your wallet, not yet in the on-hold queue.3. On-hold queue
userStakesOnHold[user] array. Entries are only created on certain early closes (closedAt < endsAt) with releaseAt = startOfTheDay(closedAt) + 1 day. After that timestamp you call withdrawUserStakes to move them to your wallet.Walkthrough
Approve + open
openSession(bidId, sessionDuration) does transferFrom(you, Diamond, amount). The whole stake moves out of your wallet into the Inference Contract for the duration of the session. The session has a scheduled endsAt derived from pricePerSecond × sessionDuration.Active
Until
endsAt (or until you close early), the session is active and your stake is reserved for inference with the chosen provider.Close — one transaction
Closing always happens in a single on-chain
closeSession call. The path splits depending on when the close happens:- Natural expiration (
closedAt ≥ endsAt) — your consumer node usually submits the close transaction itself ~1 minute afterendsAt(assuming it’s online and caught up). - Early close (
closedAt < endsAt) — you (or your app/agent) call close via API /castbefore the scheduled end. This is the first user-initiated step on the early-close path.
Token state right after close (5a vs 5b)
- 5a — Natural expiration
- 5b — Early close
No on-hold row, no timelock. The contract
safeTransfers your share back to your wallet inside the same closeSession transaction. You don’t need a separate “withdraw” step.Last mile — MOR back in your wallet (6a vs 6b)
- 6a — Natural expiration
- 6b — Early close (claim)
Already done in step 5a. No further action needed.
How the provider gets paid (and what your stake has to do with it)
Your stake is not what pays the provider in real time. InsidecloseSession:
- The contract sets
closedAtand marks the session inactive. _rewardUserAfterClose— your share is computed and eithersafeTransfer’d to your wallet (natural expiration) or split between an immediate transfer and anuserStakesOnHoldrow (early close)._rewardProviderAfterClose— pays the provider for time actually used. For typical staked sessions, the payment comes from the protocol’s separatefundingAccountviatransferFrom, not from your stake in that same step.
endsAt.
States (with the on-hold queue made explicit)
What “recover” really means
Older docs (and even some early Morpheus discussion) used the word “recover” loosely. There is no singlerecover RPC. There are two distinct on-chain calls:
closeSessionstops the session and triggers refund logic.withdrawUserStakesis the separate claim action for early-close timelocked balances.
endsAt), the resolution is still a successful closeSession followed, if needed, by withdrawUserStakes. There is no other path.
On-chain calls (consumer, via proxy-router)
| Action | Endpoint |
|---|---|
| List models | GET /blockchain/models |
| Open session | POST /blockchain/models/:id/session |
| List sessions for a wallet | GET /blockchain/sessions/user?user=0x… |
| List session IDs only (lighter) | GET /blockchain/sessions/user/ids?user=0x… |
| Fetch one session | GET /blockchain/sessions/0x… |
| Close a session | POST /blockchain/sessions/0x…/close |
| Claim early-close on-hold balance | No HTTP route — call withdrawUserStakes on the Diamond contract directly |
Read-only wallet check (off-site)
tech.mor.org/session.html has a hosted read-only wallet checker that shows your MOR split across the three buckets (wallet / active session / on-hold). Use it whenever the wallet balance “looks wrong.”Minimums (from the contract)
- Consumer session open:
5MOR minimum. - Bid price floor:
10000000000wei/sec (0.00000001MOR/sec). - Provider stake:
0.2MOR (or10000MOR for a subnet provider).

