VORQ Docs
Concepts

Job lifecycle

The job state machine, job ids, ended causes, and the clocks that bound each transition.

A job lives in JobRegistry. It is created by post, funded by claim, and resolved exactly once by submitAndSettle, fail, reclaim or cancel.

                   post                 claim
     (absent) ──────────────► Open ──────────────► Claimed
                               │                     │
        cancel (owner-signed)  │                     ├─ submitAndSettle ─► Settled
                               ▼                     │       state 2, endedBecause 1
                        Cancelled                    ├─ fail (provider-signed) ─► Cancelled
                        state 3, endedBecause 2      │       state 3, endedBecause 3
                                                     └─ reclaim (anyone, past the SLA) ─► Cancelled
                                                             state 3, endedBecause 4

JobState values: 0 Open, 1 Claimed, 2 Settled, 3 Cancelled. cancel, fail and reclaim all end in Cancelled; endedBecause tells them apart.

Job ids

jobId = keccak256(abi.encodePacked(owner, c)), where c is the client's 32-byte commitment to the task payload. Rows are never deleted, so a c that has been posted once is spent for that owner: a second post reverts DuplicateJob. The persistent terminal row is also the replay guard for every job operation.

The task location (taskCid) and the result location (resultCid) are unsigned call parameters recorded on the row. Only c binds the payload; see Security model.

Ended causes

CodeConstantWith stateWhere it appears
0ENDED_NONEOpen or Claimedlive job
1ENDED_SETTLEDSettledgetJob only; settlement emits Settled, not Ended
2ENDED_CANCELLEDCancelledstored; Ended(jobId, 2)
3ENDED_PROVIDER_FAILCancelledstored; Ended(jobId, 3)
4ENDED_RECLAIMCancelledstored; Ended(jobId, 4)
5ENDED_EXPIREDCancelledgetJob only; computed at read time

An Open job past expiresAt reads as Cancelled / 5 from getJob with no transaction. Storage still says Open, and the job can still be cancelled; a landed cancel stores 2, which then replaces the computed 5.

Clocks

ClockRule
Expirypost requires block.timestamp < expiresAt <= block.timestamp + MAX_EXPIRY (86,400 s). A job is expired when block.timestamp > expiresAt; a claim landing exactly at expiresAt succeeds.
SLAThe deadline is claimedAt + slaSecs. submitAndSettle is refused after it (SlaExpired); reclaim is allowed only after it (SlaNotExpired before). slaSecs must be allowed at post (the constructor allows 3600 and 86400).
Fail graceFAIL_GRACE = 300 s. A fail landing at or before claimedAt + 300 costs no reputation; later it costs 40. Measured at landing time, not at issuedAt.
Op freshnessClaim, Settle, Fail and Cancel require issuedAt within ±600 s of block.timestamp, inclusive (StaleOp otherwise).

fail is not bounded by the SLA. After the SLA it races reclaim; payout and penalty are identical and only the cause (3 or 4) differs.

Who can move a job

TransitionAuthorised byNotes
postclient's Order signatureChecks the SLA, expiry window, model exists and is enabled. designated is not checked here.
claimoperator's Claim signatureProvider must be listed, allowed the model, match designated if nonzero, and be under effectiveCap. The model's enabled flag is not re-checked, so jobs posted before a model is disabled can still be claimed.
submitAndSettleSettle signature from the claiming providerCompared by provider id, so a key rotated after the claim still settles.
failFail signature from the claiming providerSame id comparison.
reclaimnobodyAnyone, strictly after the SLA.
cancelowner's Cancel signatureOpen only. A no-op on an already Settled or Cancelled job; NotCancellable on Claimed.

When designated == 0, any eligible provider can claim, and a fail within the grace window costs that provider nothing. The job ends with the client refunded less the gas fee, and c spent.

Funds for each exit are on Escrow and fees. Check order and revert behaviour for each function are on the JobRegistry reference.

On this page