States
Transitions and side effects
| From | To | Trigger | On-chain effect | Wallet visible |
|---|---|---|---|---|
[*] | Opening | Consumer calls openSession(bidId, duration) | Tx submitted | Pending |
Opening | Open | Tx mined | transferFrom(you, InferenceContract, stake) | Wallet −stake |
Open | ClosedNatural | closeSession mined with closedAt >= endsAt | _rewardUserAfterClose safeTransfers your share back; _rewardProviderAfterClose pays provider from fundingAccount | Wallet +full share (in same txn) |
Open | ClosedEarly | User calls closeSession before endsAt | Same two reward functions; user share split between immediate transfer and a userStakesOnHold row | Wallet +immediate part |
ClosedEarly | OnHold | (within close txn) _rewardUserAfterClose pushes a slice to userStakesOnHold[you] | releaseAt = startOfDay(closedAt) + 1 day | None until claim |
OnHold | [*] | withdrawUserStakes(you, iterations) after releaseAt | Releasable rows safeTransfer’d to your wallet | Wallet +held part |
Open | StuckPastEndsAt | Time passes without successful closeSession | None — session record unchanged | None |
StuckPastEndsAt | ClosedNatural | closeSession finally mined | Same as the direct Open → ClosedNatural path | Wallet +full share |
Definitions
- Natural expiration —
closedAt >= endsAt. No on-hold row, no claim step needed. Your share lands in your wallet inside the close transaction. - Early close —
closedAt < endsAt. Some slice of the stake may go touserStakesOnHold[you]. The rest comes back immediately. After the timelock you callwithdrawUserStakes. userStakesOnHold— array on the Inference Contract. Each entry has an amount and areleaseAt.getUserStakesOnHold(addr, iter)returns(hold_, available_).releaseAt—startOfTheDay(closedAt) + 1 day(UTC). In practice ≈ “after the end of the next full UTC day from your close.”fundingAccount— separate protocol wallet. Pays providers viatransferFrominsidecloseSession. If empty or under-approved, everycloseSessionfails.- Stuck session —
closedAt == 0pastendsAt. The close transaction has not succeeded yet (failure modes: funding account, gas on the closing wallet, consumer node offline).
Common LLM mistakes (prevent these)
- ❌ “Open a session, then call
recoverto get MOR back.” → Norecoverfunction exists. The two distinct calls arecloseSessionandwithdrawUserStakes. - ❌ “MOR returns automatically on session expiry.” → Only after a successful
closeSessiontransaction. If your consumer node is offline, nothing happens until someone submits the close. - ❌ “Closing returns only the unused portion.” → For natural expiration, the contract returns your full share inside the same txn (no on-hold row). For early close, a slice may be parked in
userStakesOnHold; the rest comes back immediately. - ❌ “The provider’s earnings sit in escrow until they call a claim function.” → Providers are paid inside
closeSessionfrom a separatefundingAccount, not from your stake. There is no separate “provider claim” for typical staked sessions. - ❌ “Closing MorpheusUI closes my sessions.” → No. The UI is just a client; the on-chain session is independent. Close explicitly via API or wait for
endsAt+ consumer node auto-close. - ❌ “I closed early, I should see all my MOR back instantly.” → A slice may be in
userStakesOnHold— callwithdrawUserStakesafterreleaseAt.
Concrete API calls (consumer side, via proxy-router)
| Action | Endpoint |
|---|---|
| Open | POST /blockchain/models/:id/session body: {"sessionDuration": 600, "failover": false, "directPayment": false} |
| List sessions for a wallet | GET /blockchain/sessions/user?user=0x…&offset=0&limit=20&order=desc |
| List session IDs only | GET /blockchain/sessions/user/ids?user=0x… |
| Fetch one session | GET /blockchain/sessions/0x… |
| Close | POST /blockchain/sessions/:id/close body: {} |
| Claim early-close on-hold balance | No HTTP route. Send withdrawUserStakes(addr, iterations) to the Diamond contract via cast send or wallet UI. |
proxy-router/docs/swagger.yaml.
Direct on-chain calls (when you need to bypass the node)
withdrawUserStakes selector: 0xa98a7c6b.
On-chain minimums
- Consumer session-open stake floor: 5 MOR.
- Provider stake (refundable): 0.2 MOR (or 10000 MOR for subnet).
- Bid price floor:
10000000000wei/sec (0.00000001MOR/sec).

