The hosted checker at tech.mor.org/session.html is useful for reading the three on-chain buckets. Contract behavior in this repo is authoritative if that page lags (especially the day-lock rules below).
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. Holds the used stipend after close until releaseAt = startOfTheDay(min(closedAt, endsAt)) + 1 day. Cleared via withdrawUserStakes.Walkthrough
1
MOR in your wallet
Standard ERC-20 balance, like any token.
2
Approve + open
openSession does transferFrom(you, Diamond, amount). The whole stake moves out of your wallet into the Inference Contract for the duration of the session. Scheduled endsAt comes from stake → daily stipend ÷ pricePerSecond (capped by max session duration).3
Active
Until
endsAt (or until you close early), the session is active and your stake is reserved for inference with the chosen provider.4
Close — one transaction
Closing always happens in a single on-chain
closeSession call — either your consumer node ~1 minute after endsAt (if online), or you closing early via API / UI.Refund math does not treat “late” vs “early” as a special case that skips the lock. It anchors to when the session stopped consuming compute: sessionEnd = min(closedAt, endsAt).5
Token state right after close
Inside
_rewardUserAfterClose:- Unused stake is
safeTransfer’d to your wallet in the same transaction. - Used stipend (session time ×
pricePerSecond, converted back to stake) is pushed touserStakesOnHoldwithreleaseAt = startOfTheDay(sessionEnd) + 1 day, if you close before that timestamp.
endsAt on the same UTC day) does day-lock the used portion. Only a close that lands after releaseAt (e.g. days late) returns everything with no hold.6
Claim the day-locked slice
After
releaseAt (≈ after the end of the UTC day the session ended on), call withdrawUserStakes(yourAddress, iterations) on the Diamond contract. There is no HTTP route for this on the proxy-router today — use cast send, MetaMask “Interact with contract”, or your wallet’s contract UI.7
Done
Spendable MOR is back in your wallet — unused immediately after close, used portion after the day-lock + claim.
Consumer capital expectation (important)
The used portion of stake is not reusable the same UTC day. Plan float accordingly:- Opening many short sessions that each “use” most of their stake will park that capital until the next day +
withdrawUserStakes. - Letting sessions expire naturally does not free the used stake for immediate reuse — the node auto-close still day-locks it.
- Size your wallet for the gross stake you need in a day of concurrent / sequential sessions, not for recycling one pile of MOR many times before midnight UTC.
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— unused stake returns immediately; used stipend may go touserStakesOnHold._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 day-locked 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)
See API endpoints for full curl examples.
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).