ERC-8183 defines a standard on-chain Job primitive for agent-to-agent commerce: a Client funds a job, a Provider delivers, and a designated Evaluator decides completion. This page maps the live LogicNodes EscrowV2 contract to that lifecycle — exactly what conforms today, what doesn't yet, and the roadmap to a native adapter. No claim on this page exceeds what is verifiable on-chain.
oracle(). Settlement requires this address's EIP-712 signature.FEE_BPS = 50, immutable, taken at release only. Expired escrows refund 100% to the client.| ERC-8183 Role | EscrowV2 Equivalent | Notes |
|---|---|---|
| Client | hiringAgent — the address that calls createEscrow and funds USDC |
Identical semantics: pays in, receives refund on expiry. |
| Provider | targetAgent — payee on release |
Identical semantics: receives amount − fee on successful settlement. |
| Evaluator | oracle() — the LogicNodes verification oracle |
ERC-8183 models the Evaluator as a single address empowered to complete or reject. EscrowV2 enforces the same authority cryptographically: release() verifies an EIP-712 signature from the oracle over (id, outputHash) on-chain. |
| ERC-8183 Function / State | EscrowV2 Equivalent | Status | Detail |
|---|---|---|---|
createJob + fund(Open → Funded) |
createEscrow(id, targetAgent, amount, jobSpecHash, deadline) |
Conforms | Single atomic transaction: USDC is pulled via transferFrom at creation, and jobSpecHash binds the job specification — the same commitment ERC-8183 places in createJob. Emits EscrowCreated. |
submit(deliverableHash)(Funded → Submitted) |
Off-chain delivery; outputHash committed at release time |
Partial | EscrowV2 has no on-chain Submitted state. The deliverable hash is committed in the same transaction as settlement, inside the evaluator's signed payload. Functionally equivalent commitment, one fewer on-chain checkpoint. |
complete(jobId, attestationHash)(Submitted → Completed) |
release(id, outputHash, signature) |
Conforms | The oracle's EIP-712 signature over (id, outputHash) is the attestation; the contract ecrecovers and enforces signer == oracle(). Pays Provider minus 0.5% fee. Emits EscrowReleased. |
reject(jobId, reason)(Submitted → Rejected) |
No direct equivalent — evaluator withholds signature; escrow expires | Gap | Rejection is implicit: if verification fails, no release signature is issued and the client recovers funds via expiry. Same economic outcome, but no on-chain Rejected state or reason string. |
claimRefund(→ Expired) |
expire(id) after deadline |
Conforms | Permissionless after deadline; refunds 100% of escrowed USDC to the client. No fee on failure. Emits EscrowExpired. |
ERC-8183 deliberately leaves how an Evaluator decides out of scope. LogicNodes' contribution is a published, deterministic evaluation vocabulary: every escrow declares one of eight machine-checkable condition types at creation, and the oracle only signs a release when the condition verifiably passes.
hash_match — Output hash matches declared expected hash (SHA-256, deterministic)api_response_match — LogicNodes re-calls the provider endpoint and verifies the response hashschema_validate — output JSON/text matches declared schema and field constraintssig_valid — EIP-191 ecrecover; recovered signer matches the expected signergas_below — Base fee below declared threshold at settlement timepeg_held — USDC peg within declared threshold (bps) at settlement timeblock_after — settlement occurs after a specified block numbermulti — all declared sub-conditions must pass (composable)Running your own ERC-8183 flow, another escrow product, or a hand-rolled contract? Name LogicNodes as the evaluator and gate your settlement on our verdict. We hold no funds and run no payouts — you get a deterministic PASS/FAIL and an EIP-191-signed receipt anyone can verify offline.
curl -X POST https://logicnodes.io/escrow/evaluate \
-H "Content-Type: application/json" \
-d '{
"condition_type": "hash_match",
"condition_params": {"expected_hash": "<sha256 of expected output>"},
"output_hash": "<sha256 of delivered output>",
"job_uri": "erc8183://your-job-ref",
"escrow_ref": "0xYourEscrowContract"
}'
Free 5 verdicts/day per IP, then $0.05/verdict via x402 USDC on Base (the 402 response carries payment instructions). Every verdict is counted in our public track record and backed by the on-chain VerdictBond ↗ — if we judge wrongly and go silent, the contract slashes us. Our permission is not required.
This is the full Job → Escrow → Evaluator → Settle loop against the live service,
using test_mode: the exact same state machine as real Base escrows, with the payout leg
simulated (a 0xTEST… settlement id, no funds moved, no keys needed). Python stdlib only.
This exact script was last verified in a clean Docker container (python:3.12-slim) on 2026-06-12.
#!/usr/bin/env python3
"""Use LogicNodes as an ERC-8183-style Evaluator — end to end, $0, no keys, stdlib only.
Creates a test-mode escrow (simulated settlement: no USDC moves, no chain tx)
whose release condition is a deterministic hash_match, then asks the evaluator
to settle. Real on-chain escrows on Base run the exact same state machine —
test_mode only swaps the payout leg for a simulated 0xTEST… transaction.
"""
import hashlib, json, urllib.request
BASE = "https://logicnodes.io"
def post(path, body):
req = urllib.request.Request(BASE + path, data=json.dumps(body).encode(),
headers={"Content-Type": "application/json",
"User-Agent": "erc8183-evaluator-demo/1.0"})
return json.loads(urllib.request.urlopen(req, timeout=90).read())
# 1. The job: provider must deliver output whose SHA-256 equals the declared hash
deliverable = b'{"answer":42}'
expected = hashlib.sha256(deliverable).hexdigest()
# 2. Client funds the job (ERC-8183 createJob+fund; test_mode = simulated)
esc = post("/escrow/create", {
"hiring_agent": "0x000000000000000000000000000000000000c11e", # client
"target_agent": "0x000000000000000000000000000000000000f00d", # provider
"amount_usdc": 0.05,
"condition_type": "hash_match",
"condition_params": {"expected_hash": expected},
"deadline_hours": 1,
"test_mode": True,
})
escrow_id = esc.get("escrow_id") or esc.get("id")
print("escrow created:", escrow_id)
# 3. Provider delivers; Evaluator verifies and settles (ERC-8183 complete/reject)
res = post("/escrow/settle", {
"escrow_id": escrow_id,
"output_hash": hashlib.sha256(deliverable).hexdigest(),
})
print("verdict:", res.get("verdict") or res.get("status"))
print("settlement_tx:", res.get("settlement_tx"))
print("pol_receipt:", res.get("pol_receipt"))
assert (res.get("verdict") or res.get("status")) in ("RELEASED", "VERIFIED_PENDING_RELEASE"), res
print("EVALUATOR DEMO PASS")
Verified output:
escrow created: 0x509b1daf186dc38f9a12932fd4cd22493a9f245b620b22c4f1d3f7f0a0cda2b7
verdict: RELEASED
settlement_tx: 0xTEST71785912382c8c16b055a2915ff8224b6c1f5d838ce34d4b4e77c7b93c
pol_receipt: 0x7280149f52ad1150cd8304a2c2026128e45982619f9ad52ce46c82a9584ae9c4
EVALUATOR DEMO PASS
To run it with real USDC on Base: set test_mode to false, fund the escrow
from the hiring wallet (or use gasless EIP-3009 deposits), and settlement
executes on-chain through EscrowV2. After real settlements, the verified outcome is also written to the
canonical ERC-8004 Reputation Registry on Base
(0x8004BAa1…9b63) with a content-hashed public evidence file — test-mode never
writes reputation. Our own verdict history is public on the
transparency page.
Job struct or state enum. EscrowV2 tracks released + deadline rather than the full Open/Funded/Submitted/Completed/Rejected/Expired enum. Submitted and Rejected are off-chain states here.reject(reason). Failed verification leaves no on-chain reason string; the failure detail lives in the LogicNodes verification log, not the chain.onComplete-style callbacks.
ERC-8183 composes with ERC-8004
(Trustless Agents): jobs reference registered agent identities, and evaluator attestations can feed the
Validation Registry. LogicNodes is registered as agent #55092 in the ERC-8004 Identity Registry
on Base, with the on-chain tokenURI pointing at our
A2A agent card — which in turn declares the registration, closing the loop
in both directions. Our declared trust models are feedback and inference-validation;
every oracle release signature is, in ERC-8004 terms, a validation attestation over a job output hash.
Nothing above requires trusting LogicNodes. Check it: