Skip to content

Delegator workflow

As a delegator, you define the task, fund its maximum reward, and choose the signed bid that becomes the on-chain agreement.

The transaction fragments below use ethers v6 contract instances connected to the delegator’s signer. Values such as specHash, inputHash, amounts, deadlines, and specURI come from the task prepared in step 1.

Prepare the complete TaskSpec and task input off-chain. For STATS_V1:

  • canonicalize the TaskSpec with RFC 8785 JCS, then compute specHash = keccak256(JCS(taskSpec));
  • encode the uint64[] input with abi.encode, then compute inputHash = keccak256(abi.encode(values));
  • express every USDC amount as an integer with 6 decimals; and
  • choose acceptDeadline, maxComputeWindowSec, and a minBondUSDC at or above the hub’s current minimum.

See TaskSpec commitments and STATS_V1 for the exact encodings.

createTask transfers real USDC into the hub. Confirm the chain, hub address, current fee, verifier route, balance, allowance, and deadlines first. Approve only the exact amount needed, simulate the complete call with staticCall, then submit it:

const createArgs = [
specHash,
statsV1VerifierId,
inputHash,
maxRewardUSDC,
minBondUSDC,
acceptDeadline,
maxComputeWindowSec,
specURI,
] as const;
await (await usdc.approve(hubAddress, maxRewardUSDC)).wait();
await hub.createTask.staticCall(...createArgs);
const receipt = await (
await hub.createTask(...createArgs)
).wait();

Simulation checks the call against the current state but does not reserve that state. Re-read time-sensitive values before sending the transaction.

Read TaskCreated from the receipt to obtain the taskId. The hub holds the maximum reward in USDC and snapshots the active verifier for that task.

Receive bids off-chain and confirm each bid matches the created task:

  • taskId, specHash, and verifierId match the on-chain task;
  • rewardUSDC, bondUSDC, and computeWindowSec match the terms you want;
  • validUntil is current; and
  • nonce matches bidNonce[delegatee].

The delegatee signs the exact EIP-712 bid. Review signed bids for the domain and field order.

Encrypt the task key for the chosen delegatee and call:

await (
await hub.selectBid(taskId, bid, delegateeSignature, encryptedKey)
).wait();

Selection records the reward, bond, compute window, and fee snapshot. The hub immediately returns escrowedRewardUSDC - rewardUSDC to you and keeps the selected reward in escrow. TaskSelected carries the encrypted key for the delegatee, which retains control of activation.

The encrypted-key byte format is bilateral and outside the on-chain protocol. Agree on the encryption, key-wrap, nonce, and associated-data encoding with the delegatee, and test a round trip before selection. Follow Identity, key delivery, and capabilities for the interoperable baseline.

If a selection is cancelled, the task keeps its current escrow for reselection. OCH-1.1 has no top-up call, so every later bid must set rewardUSDC <= escrowedRewardUSDC.

Watch the task and its lifecycle events:

  • TaskActivated starts the selected compute window after the delegatee posts the bond.
  • TaskCompleted carries the output commitment, complete publicValues, and output URI. A valid proof releases the selected reward, less the snapshotted fee, and returns the bond to the delegatee.
  • TaskSelectionCancelled returns a selected task to OPEN, ready for another valid bid within the task’s remaining escrow and terms.
  • TaskTimedOut records the ACTIVE timeout path, which returns the reward and full bond to you.

Use proof binding to verify the completion payload and events and limits for the exact event fields.

6. Recover funds when a task does not complete

Section titled “6. Recover funds when a task does not complete”

Exits are explicit transactions. Call the path that matches the task state, or let another permitted caller do so:

State Call Caller and timing Funds result
OPEN cancelUnaccepted(taskId) Delegator, while the task is open Returns current reward escrow to the delegator.
OPEN sweepExpiredOpen(taskId) Any caller after acceptDeadline Returns current reward escrow to the delegator.
SELECTED cancelSelection(taskId) Delegator at any time, or any caller after activateDeadline Returns the task to OPEN; current reward escrow remains available for reselection.
ACTIVE claimTimeout(taskId) Any caller after submitDeadline Returns reward escrow and transfers the full bond to the delegator.
await (await hub.cancelUnaccepted(taskId)).wait();
await (await hub.sweepExpiredOpen(taskId)).wait();
await (await hub.cancelSelection(taskId)).wait();
await (await hub.claimTimeout(taskId)).wait();

Read the task first and submit only the one call valid for its current state and deadline.