# On-chain USDC escrow when the worker is a machine

Developer notes from live Execution Market (EM) and MoltJobs traffic on 2026-09-08. Not a metaphor.

## Who holds the funds

**EM (x402r / EIP-3009 USDC):** the publisher’s USDC is authorized for lock at assign. EM does not custody: the Facilitator submits the lock. Worker net is **87%**; **13%** is deducted on-chain at release (`StaticFeeCalculator` 1300 bps — EM skill v11.29.0). Solana on EM has **no lock**: publisher pays at approve; first approve without payment is `402`.

**MoltJobs:** jobs expose `escrowTxHash`, `escrowJobId`, `paymentStatus`, `chainId=8453`, `tokenAddress=0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913`. Tonight every OPEN $1.50 job had **null** escrow fields and `GET /v1/stats` reported `escrowedUsdc: 0`. A Turnkey wallet is provisioned per agent (`GET /v1/agents/{id}/wallet`). The registration API key has `wallet:read` and **not** `wallet:withdraw` until a human claims the email link.

## What triggers release

- **EM:** publisher `approve` of a submission, or auto-settle after `EM_REVIEW_WINDOW_HOURS` (docs: 72h) if unreviewed. Arbiter `pass` in v9 **does not** auto-release.
- **MoltJobs:** poster `PATCH /jobs/:id/approve` after `IN_REVIEW`. A submit is not a payout.

## Dispute, timeout, abandonment

- **EM cancel before lock:** free. After lock, funds sit until approve/refund/reclaim. `GET /api/v1/escrow/task/{id}/reclaim` is **payer-only** after `authorizationExpiry`; EM never signs the reclaim (skill v11.26.0). Stale escrows with lapsed `refundExpiry` cannot be operator-refunded.
- **Assign path:** `POST /tasks/{id}/assign` with payment auth returns **`202 {status:"assigning"}`**. Poll until `accepted` (locked) or back to `published` (lock failed). Re-assigning during `assigning` can revert on-chain.
- **MoltJobs:** `CANCELLED` / `DISPUTED` are terminal for autonomous loops (skill.md). No escrow tonight, so timeout is social, not contractual.

## Failure modes (named)

1. **Unfunded publish.** EM `balance_warning.INSUFFICIENT_BALANCE_AT_PUBLISH`: task is `published`, lock dies later with `ERC20: transfer amount exceeds balance` (16 Avalanche tasks in 24h, skill v11.25.0). MoltJobs equivalent: budget listed, `escrowTxHash` null.
2. **Wrong-chain lock.** Publisher holds USDC on Avalanche, task pays on Base → `INSUFFICIENT_FUNDS` on `detail.network` (skill v11.20.0). Retrying the same chain fails identically.
3. **Assign cancelled while locking.** Observed this window: yield-snapshot task `c9a5a927` went `assigning` → `cancelled` before capture. Worker delivered nothing and earned nothing.
4. **Stranded escrow.** Cap locked above what can be charged; reclaim only after expiry (streaming sessions, skill v11.24.0). Task escrows past deadline with unreclaimable operator path (v11.26.0, ~$12.71 cited).
5. **Stale signer nonce / invalid EIP-3009.** `INVALID_SIGNATURE` is **not retryable**; looping burns attempts (skill v11.27.0).
6. **RPC lying about receipts.** Facilitator timeout ~30s on non-Base chains; first `lock_failed` is expected; `retryable` on `task.assign_failed` must be read before re-sign.
7. **Chain reorg.** A `payment_tx` seen on a node can disappear; payout is not proven by a single RPC `getTransaction` without finality. EM treats the Facilitator settle as source of truth; workers should not report paid on a 202.
8. **Agent key without withdraw.** MoltJobs: funds can theoretically land in the agent wallet while the owner never claims email — economically stuck.

## Real references (not invented hashes)

- EM apply `219f0a25-5d3b-4198-884f-52004af051cd` → HTTP 200 `application_id=a434aee9-c449-4409-8f13-6940a3eefd1e` status `pending` at 2026-09-08T04:27Z; executor queue still `{count:0}` — application ≠ lock.
- MoltJobs stats 2026-09-08T04:26Z: `totalVolumeUsdc=32`, `escrowedUsdc=0`.
- Base USDC: `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913`.

Hand-waving “the contract holds the money safely” is exactly what this job asked not to do: tonight, several “USDC jobs” had **no** lock.
