Agents pay agents. On-chain. No humans in the loop.
π Live Demo Β· π Architecture Β· π‘ API Reference Β· SDK Docs
Important
What this is: A deployed Casper testnet escrow console for AI-agent payments: signed x402 payment intent, Casper deploys for escrow lifecycle calls, Neon-backed hosted records, IsolationForest risk scoring, ML-KEM metadata encryption, and VRF-assisted arbitration. Console live at ae402.xyz; API live at agentescrow402-api.onrender.com.
Table of contents
The x402 protocol defines machine-to-machine payments via HTTP 402 headers. Existing implementations assume Ethereum facilitators. AgentEscrow402 brings the pattern to Casper Network with live testnet escrow calls plus a hosted console that labels what is on-chain, what is Neon-backed API state, and what is demo-signer convenience for browsers.
| Feature | AgentEscrow402 | Coinbase x402 | Manual invoicing |
|---|---|---|---|
| Trustless on-chain escrow | β Time-locked WASM contract | β Facilitator holds funds | β N/A |
| Reputation tracking | β Exponential decay, per-agent | β None | β None |
| Multi-sig dispute resolution | β 3-of-5 arbiter vote | ||
| Zero human facilitation | β Fully agentic | β Always human | |
| Casper Network native | β WASM contract | β EVM only | β |
| Multi-asset escrow | β CSPR, CEP-18, CEP-78 (real on-chain) | β Single asset | β |
| Atomic secret-for-payment swap | β SHA-256 HTLC commit/reveal | β Not supported | β |
| AI-assisted dispute triage | β Evidence scoring feeds the arbiter vote | β None | β None |
| Sybil-resistant agent identity | β DID registry, staking + slashing | β None | β |
| Post-quantum metadata confidentiality | β ML-KEM-768 hybrid encryption | β None | β |
| Production maturity / ecosystem adoption | β Live, mainnet, adopted by real facilitators | β Universally understood |
See what's real vs. simulated for exactly which of these are live on-chain today versus API-level.
Four steps, no human signature required mid-flow (see verified on-chain transactions and what's real vs. simulated for the exact scope of that claim).
- Agent A creates escrow β locks funds in a time-locked Casper contract with a TTL and service hash
- Agent B verifies payment β checks the x402 header against the on-chain escrow before serving work
- Agent A releases β confirms delivery; funds transfer to Agent B; reputation score updates
- Timeout protection β if Agent B never delivers, Agent A reclaims funds after TTL expires; no arbiter needed
Agent A Payment Server Casper Network
β β β
ββ POST /escrow βββββββββββββΆβ β
β {receiver, amount, ttl} βββ create_escrow() ββββββββββΆβ
β β β funds locked
ββββ 201 {service_hash} βββββ€ β
β β β
ββ GET /api/compute βββββββββΆβ β
β X-Payment: x402-v1;... βββ verify header ββββββββββββΆβ
β βββ ok βββββββββββββββββββββββ€
ββββ 200 result βββββββββββββ€ β
β β β
ββ POST /release ββββββββββββΆβββ release() ββββββββββββββββΆβ
β {service_hash} β β funds β Agent B
ββββ 200 ββββββββββββββββββββ€ reputation updated on-chain β
x402 header format: X-Payment: x402-v1;<escrow_hash>;<amount>;<sender>;<timestamp>;<nonce>;<signature>
Protected endpoints return 402 Payment Required with machine-readable terms when the header is missing.
This is the base happy-path lifecycle. The dispute-resolution vote, HTLC atomic-swap commit/reveal, and multi-asset (CEP-18/CEP-78) flows each have their own diagram in docs/ARCHITECTURE.md rather than being crammed into this one.
Every escrow moves through the same on-chain states regardless of which exit path it takes β created, then released, refunded, or disputed β resolved:
stateDiagram-v2
[*] --> Pending: create_escrow()\nfunds locked, TTL set
Pending --> Released: release()\n(sender confirms delivery)
Pending --> Refunded: refund()\n(caller, after TTL expiry)
Pending --> Disputed: dispute()\n(sender or receiver)
Disputed --> Released: resolve()\n3-of-5 arbiter vote β payee
Disputed --> Refunded: resolve()\n3-of-5 arbiter vote β sender
Released --> [*]
Refunded --> [*]
Text fallback: an escrow starts Pending; it exits via release (seller paid),
refund (TTL expired, nobody delivered), or dispute β 3-of-5 arbiter resolve, which
routes funds to either side. commit_swap/reveal_swap is a variant exit on the same
Pending state β see the HTLC atomic-swap flow below.
All escrow types share the same on-chain core but add specialised flows on top:
flowchart LR
subgraph Escrow Types
A["π Standard\nCSPR escrow"]
B["πͺ Multi-Asset\nCEP-18 / CEP-78"]
C["π Streaming\nLinear vesting"]
D["π Atomic Swap\nHTLC commit-reveal"]
E["π¦ Batch\nBulk create/release"]
end
subgraph On-Chain Core
F["Smart Contract v9\n(8 entry points)"]
end
subgraph Security Layer
G["π‘οΈ Release Cap\n+ Arbiter Quorum"]
H["π² VRF Election\nOn-chain randomness"]
I["π¦ Insurance Pool\nRisk-scaled premiums"]
J["π Identity Registry\nDID + reputation"]
end
A & B & C & D & E --> F
F --> G & H & I & J
The escrow contract's sender key is not, by itself, sufficient authorization to move
funds above a configurable cap. This is enforced on-chain in
contracts/escrow/src/main.rs, not in the API layer:
- A
release_capnamed key (RELEASE_CAP_KEY) defaults to1_000_000_000_000motes = 1,000 CSPR (DEFAULT_RELEASE_CAP_MOTES) and is retunable on-chain viaset_release_cap(installer-only) with no redeploy needed. - Both fund-releasing entry points β
release()andreveal_swap()(the HTLC path below) β callread_release_cap()and compare it against the escrow amount. Below the cap, the sender's own signed call is enough, exactly like today's happy path. - Above the cap, the sender's signature alone is rejected. The caller must additionally
supply
arbiter_pubkeys/arbiter_signaturescovering a canonical"release:<service_hash>:cap_approval"(or"reveal_swap:...") message.require_arbiter_cap_approval()βverify_arbiter_quorum()checks that at leastthreshold(default 3, same registered-arbiter listresolve()uses) distinct, registered arbiters produced valid Ed25519 signatures over that exact message β dedup'd, so one arbiter can't sign twice to fake a quorum. Fail the quorum check and the call reverts withERR_CAP_EXCEEDED; no funds move. - In other words: propose β arbiter-quorum approve β release, not agent key β release, for anything above the cap. A compromised or malicious agent key can drain at most the cap amount unilaterally; anything larger requires collusion or compromise of a majority of the independently-held arbiter keys.
- Confirmed on the deployed v9 contract: the
resolve(3-of-5 multisig) gas-benchmark entry in docs/GAS_BENCHMARK.md measures the same signature-verification code path this guard reuses (~7.5 CSPR gas for 3 on-chain Ed25519 verifications vs. ~3.2 CSPR for a plainrelease).
flowchart TD
A["release() / reveal_swap() called\namount known"] --> B{"amount > release_cap?"}
B -- "no (below cap)" --> C["Direct release\nsender signature only"]
C --> D["funds β receiver"]
B -- "yes (above cap)" --> E["Sender proposes:\nsubmit arbiter_pubkeys +\narbiter_signatures"]
E --> F{"verify_arbiter_quorum()\n>= threshold distinct,\nregistered, valid sigs?"}
F -- "no" --> G["revert ERR_CAP_EXCEEDED\nno funds move"]
F -- "yes" --> D
Text fallback: below the configurable cap, the sender's own call releases funds directly; above it, the sender's call is rejected unless it also carries a quorum of registered-arbiter Ed25519 signatures over the exact release/service-hash message β otherwise the transaction reverts and no funds move.
This is a structural safety property, not a policy promise: it's enforced by the contract
bytecode itself for release/reveal_swap. The escrow-manager's batch_release/batch_cancel
entry points are wired to API endpoints with a server-side cap/quorum guard β see
docs/STATUS_AND_ROADMAP.md.
Under 5 minutes for local development. No Casper node needed β sandbox mode is default locally.
Prerequisites: Python 3.11+
git clone https://github.com/alexbelij/AgentEscrow402.git
cd AgentEscrow402
pip install -r requirements.txt
cp .env.example .env
python -m uvicorn server.app:app --host 0.0.0.0 --port 8000Verify:
curl http://localhost:8000/health
# {"status":"ok","sandbox":true,"db":"disconnected",...}curl -X POST http://localhost:8000/escrow \
-H "Content-Type: application/json" \
-H "X-Payment: x402-v1;<service_hash>;5000000;<sender>;<timestamp>;<nonce>;<signature>" \
-d '{"receiver":"agent-B","amount":5000000,"service_hash":"<64-hex>","ttl":300}'
# {"service_hash":"<64-hex>","status":"pending",...}curl http://localhost:8000/escrow/<service_hash>
# {"service_hash":"<64-hex>","status":"pending","amount":5000000,...}curl -X POST http://localhost:8000/release \
-H "Content-Type: application/json" \
-H "X-Payment: x402-v1;<service_hash>;5000000;<sender>;<timestamp>;<nonce>;<signature>" \
-d '{"service_hash":"<64-hex>"}'
# {"status":"released",...}Tip: Switch to testnet by setting
CASPER_NODE_URLandDEPLOYER_KEY_PATHin.env.
Judging a hackathon project means separating "works in a demo" from "works on-chain." Here's the
honest breakdown, verified against the current deployment (contract package d3ca33d1...c8eeb,
version 9, updated 2026-07-07):
| Component | Status | Evidence |
|---|---|---|
| Escrow create / release / refund / dispute / resolve | β Real on-chain | Real Casper testnet transactions in live mode (SANDBOX=false, what production runs); see Verified on-chain |
Release cap + arbiter-quorum guard on release/reveal_swap |
β Real on-chain | Enforced in contract bytecode, not the API; see No-unilateral-withdraw guard |
HTLC atomic-swap (commit_swap / reveal_swap) |
β Real on-chain | SHA-256 commit/reveal entry points, live round-trip with cross-account reveal |
| CEP-18 (fungible token) transfers | β Real on-chain | Deployed test token AETUSD, transfer + balance read against live contract state |
| CEP-2612-inspired gasless permit (CEP-18) | β Real on-chain | Custom permit()/permit_nonce() entry points added to a forked CEP-18 contract (Ed25519-signature-gated allowance, no session-wasm needed); live-verified: owner signs an off-chain message only, relayer submits permit()+transfer_from() and pays gas, real balance moves |
| CEP-78 (NFT) mint/transfer | β Real on-chain | Deployed test collection AETNFT, mint + transfer + ownership read against live contract state |
| ID-1 Agent Identity Registry (DID + stake + reputation) | β Real on-chain, separate contract | Standalone deployed contract, not the escrow contracts; see ID-1 registry and docs/AGENT_IDENTITY_REGISTRY.md |
| x402 signature verification | β Real crypto | Ed25519 verify (cryptography lib) + nonce replay protection, not a stub |
| Reputation scoring, staking, slashing (off-chain API) | β Real logic | Exponential decay + stake-weighted slashing in identity_registry_api.py β separate from the on-chain ID-1 registry above |
Arbiter multisig resolution (resolve) |
β Real crypto | Real Ed25519 3-of-5 quorum check over the escrow/verdict payload, replay-proof |
VRF-assisted arbiter election (/vrf/elect) |
β Real on-chain election | Submits select_arbiters to the deployed vrf-arbiter contract, waits for finalization, and reads the result back; falls back to a reputation-weighted local CSPRNG only when the contract is unavailable/unconfigured or every on-chain candidate for a draw is a dispute party β see VRF-assisted arbiter election below |
Payment streaming (/escrow/stream) |
Streamed/remaining amount computed from wall-clock time in the backend; not an on-chain per-tick release yet | |
| Hosted console demo-signer | One fixed public demo identity + signature, gated by ALLOW_HOSTED_DEMO_IDENTITY, so browser visitors without a wallet can try the console β never used in the signature-verification code path for real requests |
|
| TEE-attested proofs, on-chain ZK-KYC, cross-chain bridge | β Not implemented | Roadmap ideas only β would need real TEE hardware or a ZK circuit; out of scope for the hackathon deadline, see ROADMAP.md |
POST /vrf/elect (server/vrf_election.py) picks a dispute's arbiter via a real on-chain call
to the deployed vrf-arbiter contract instead of a purely off-chain choice: it submits
select_arbiters(dispute_id, count), polls until the transaction finalizes, and reads the
elected candidates back from elections_dict. Because the contract's select_arbiters has no
notion of dispute parties, INVARIANT 5 (arbiter must not be either dispute party) is enforced by
the backend over the returned candidate list β requesting more than one candidate per election
leaves room to drop any that are also parties. 4 arbiters are currently registered on-chain via
register_arbiter (staked purses). The API response's method field is genuinely
"onchain_vrf" when this succeeds, and only falls back to "local_csprng" (a
reputation-weighted local pseudo-random choice) when the contract is unavailable/unconfigured,
or β as verified live β when every on-chain candidate returned for a given draw happens to be a
dispute party. Real testnet deploy hashes and both a normal election and that exclusion stress
case are in
docs/evidence/VRF_ONCHAIN_ELECTION.md.
Full architecture details and roadmap in docs/STATUS_AND_ROADMAP.md β kept current, not a one-time snapshot.
Concrete scenarios this unlocks today, not hypotheticals:
- Autonomous data-provider agents β an AI agent buys a single API call, dataset row, or model inference from another agent, pays through escrow, and the seller only gets funds once the buyer confirms receipt. No invoicing, no manual reconciliation, no trusted intermediary holding the money.
- Multi-agent pipelines with built-in recourse β a chain of agents (scraper β summarizer β translator) each escrow-pay the next step; if any step fails to deliver, the TTL-based refund or the 3-of-5 arbiter dispute path returns funds automatically instead of the payer eating the loss.
- Reputation-gated marketplaces β buyers can filter sellers by on-chain reputation score (exponential decay + slashing) before ever escrowing funds, so bad actors lose standing automatically rather than needing a human moderator.
- Cross-asset agent payments β an agent can be paid in native CSPR, a fungible token (CEP-18), or even receive an NFT (CEP-78) as proof-of-service, all through the same escrow lifecycle and the same x402 header.
- Atomic secret-for-payment swaps β the HTLC
commit_swap/reveal_swapflow lets a seller release a secret (an API key, a decryption key, a proof) only in the same transaction that pays them β either both happen or neither does.
Real hash-time-lock contract flow (commit_swap/reveal_swap in
contracts/escrow/src/main.rs), not a simulated swap:
sequenceDiagram
participant Sender
participant Contract as escrow contract
participant Receiver
Sender->>Contract: commit_swap(service_hash, sha256(preimage))
Note over Contract: hash-lock stored,\nnot revealed yet
Note over Sender,Receiver: off-chain: external condition met\n(e.g. secret handed over once paid elsewhere)
Receiver->>Contract: reveal_swap(service_hash, preimage,\narbiter_pubkeys, arbiter_signatures)
Contract->>Contract: sha256(preimage) == commit_hash?
alt hash matches AND (amount <= cap OR quorum verified)
Contract->>Receiver: funds released
else hash mismatch or above-cap quorum missing
Contract-->>Receiver: revert (ERR_INVALID_PREIMAGE / ERR_CAP_EXCEEDED)
end
Text fallback: the sender locks a SHA-256 hash on-chain with commit_swap; funds only move
once someone calls reveal_swap with the exact preimage β verified on-chain, not by a
trusted party β and, above the release cap, also with a valid arbiter-quorum signature set.
Live-verified on testnet with a different account revealing than the one that committed (see
Verified on-chain).
flowchart LR
A["Agent A\n(Payer)"] -->|"POST /escrow\nfunds + TTL"| MW
subgraph Server ["Payment Server β FastAPI"]
MW["x402 Middleware\nheader parse + verify"]
API["REST API"]
MW --> API
end
API -->|"create_escrow()"| ESC
subgraph Chain ["Casper Network"]
ESC["escrow.wasm\nTime-locked contract"]
REP["Reputation Store\nExp. decay scoring"]
ARB["Arbiter Pool\n3-of-5 multi-sig"]
ESC --> REP
ESC --> ARB
end
ESC -->|"funds locked"| B
B["Agent B\n(Payee)"] -->|"X-Payment header\nservice delivery"| MW
A -->|"POST /release"| API
API -->|"release() β funds β B\nreputation++"| ESC
The payment server mediates between agent SDKs and the Casper contract. In sandbox mode the Casper client is replaced by an in-memory store. In the hosted demo, API records are persisted in Neon while the console clearly labels the hosted demo signer versus production x402 signatures.
Detailed diagrams β docs/ARCHITECTURE.md
Real screenshots of the live deployment (captured 2026-07-05, not mockups):
| Homepage | Console overview |
|---|---|
![]() |
![]() |
| Escrows table | Escrow detail | Arbitration (AI dispute analysis) |
|---|---|---|
![]() |
![]() |
![]() |
Live at ae402.xyz Β· ae402.xyz/console/overview
Deployed on Casper Testnet:
| Contract | Hash | Explorer |
|---|---|---|
| Core Escrow | 612cead2226329fafec492042fd96a999df06d1e88c476913a167f44d3ddd9ec (package: d3ca33d192dda5ece798db91811ec1259d2197ca0e8d3ea4de043b977d3c8eeb, v9) |
view |
| Escrow Manager | bfa8c02cb3ab0f9d7bf03335f324973675200a597162e1e5fa4cb5a77dff675d |
view |
| Insurance Pool | e128780fd7e41159df4ca14d8584c7ef0cea2d75e6d5ba4166d94ca41f2d8929 (hardened redeploy β the old e36b958d... had a fully public claim()/withdraw(), superseded) |
view |
| VRF Arbiter | 78ae28702deeb2eadec573d95b870f68b928a82a3566e292ff33a9ae2c779c93 (package: 53805f7866cd158ff091ab93efe2f19bd2e803414a5ef1badc7a46d759f36611) β real on-chain election write path wired and live-verified, 4 arbiters registered (see VRF section) |
view |
| Agent Identity Registry (ID-1) | 1f29271d986818254d42e5551dd8fbb2e2b7f7295bdfcd6558639584ad311cae (package: 0b760bb7bf9be5a74ee4ed5626bcc74a8154f221a059e29fc9d768d45fb4a2ba, v2) β standalone DID/stake/reputation registry, separate from the escrow contracts |
view |
| MultiAssetEscrow (CEP-18) | 52db09a146158ba2a07b5da07587046985ce8ca3be094fca9ad63cb6b9ecd12a (package: a3207e9bb29f6cec6c5017e6c7538626f92f001d35cda22585dff9f76a488044) β real contract-custody escrow for CEP-18 tokens, full lifecycle verified on-chain |
view |
| CEP-18 test token (AETUSD) | 177ca5d88f72e1ca72fbe94a24ba34b03830dd1fe63d90d3d719cd6e6d4de754 |
view |
| CEP-18 test token (AEMAT) | 8ba7df6fd9a12c71de903a915717537eeff4f04adf33f4ed8abf16c254e300a5 (package: 5caa324c3073a8b9fc05076a01e9d4d658cb08a1b4839fa0aa93dac39213e3fd) β custody-compatible token (uses get_immediate_caller) |
view |
| Entry point | Description |
|---|---|
create_escrow |
Lock funds with TTL and service hash |
release |
Confirm delivery β funds to receiver (arbiter-quorum-gated above the release cap) |
refund |
Reclaim after TTL expiry |
dispute |
Open a contested payment |
resolve |
3-of-5 arbiter vote β auto-payout |
commit_swap |
HTLC atomic-swap: sender locks a SHA-256 hash of a secret |
reveal_swap |
HTLC atomic-swap: anyone who knows the secret releases funds |
configure_fee |
Set insurance pool fee (basis points) |
set_release_cap |
Update the amount above which an arbiter-quorum approval is required |
set_arbiters |
Rotate the 5-account arbiter pool (no redeploy needed) |
emergency_freeze |
Pause all state changes (installer-only) |
unfreeze |
Resume operations after emergency_freeze (installer-only) |
Security status: latest changed code was reviewed through NVIDIA API and no concrete HIGH blockers were reported for the console/Neon patch. Architecture decisions and roadmap are documented in docs/STATUS_AND_ROADMAP.md.
Base URL (production): https://agentescrow402-api.onrender.com
Full OpenAPI spec β docs/openapi.yaml
All 62 endpoints β click to expand
| Method | Path | Description |
|---|---|---|
| Core escrow | ||
GET |
/health |
Server health + mode |
GET |
/stats |
Console statistics and data-source label |
GET |
/contracts |
Deployed contract hashes and roles |
GET |
/escrows |
List escrows (filter by status, paged) |
GET |
/escrow/{hash} |
Look up escrow by service hash |
GET |
/escrow/{hash}/history |
Full state-change history of an escrow |
POST |
/escrow |
Create escrow β lock funds |
POST |
/release |
Release funds to receiver |
POST |
/refund |
Refund sender after TTL |
POST |
/dispute |
Open dispute (sender or receiver) |
POST |
/resolve |
Arbiter vote (3-of-5) |
POST |
/compute-hash |
Derive service hash from params |
GET |
/reputation/{agent} |
Agent trust score |
GET |
/agents |
List all known agents with reputation |
GET |
/estimate |
Fee + insurance estimate for an amount |
GET |
/events |
SSE stream of escrow events |
GET |
/wasm/escrow_funder |
Download escrow_funder.wasm binary |
| Batch operations | ||
POST |
/escrows/batch |
Create multiple escrows in one call |
POST |
/escrows/batch-release |
Release multiple escrows atomically |
POST |
/escrows/batch-cancel |
Cancel (refund) multiple escrows |
| Multi-asset & streaming | ||
GET |
/escrow/cep18-permit-nonce |
CEP-18 gasless permit nonce lookup |
POST |
/escrow/multi-asset |
Create CEP-18/CEP-78 token escrow |
POST |
/escrow/multi-asset/{hash}/release |
Release multi-asset escrow |
POST |
/escrow/multi-asset/{hash}/refund |
Refund multi-asset escrow |
POST |
/escrow/multi-asset/{hash}/dispute |
Dispute multi-asset escrow |
POST |
/escrow/multi-asset/{hash}/resolve |
Resolve multi-asset escrow |
POST |
/escrow/stream |
Create linear-vesting streaming escrow |
GET |
/escrow/{hash}/stream-status |
Streaming escrow vesting status |
POST |
/escrow/{hash}/stream-claim |
Claim fully-vested streaming escrow |
| Atomic swap (HTLC) | ||
POST |
/escrow/atomic-swap/commit |
Commit SHA-256 hash-lock |
POST |
/escrow/atomic-swap/reveal |
Reveal preimage to release funds |
| AI arbitration | ||
POST |
/arbitration/analyze |
AI-assisted dispute evidence analysis |
GET |
/arbitration/history |
Recent arbitration recommendations |
| VRF arbiter election | ||
POST |
/vrf/elect |
VRF-assisted on-chain arbiter election |
GET |
/vrf/election/{dispute_id} |
Look up election result |
GET |
/vrf/arbiters |
List registered arbiters |
POST |
/vrf/arbiters/register |
Register an agent as arbiter |
| Insurance pool | ||
POST |
/insurance/deposit |
Deposit into insurance pool |
POST |
/insurance/claim |
Claim from insurance pool |
GET |
/insurance/pool-stats |
Pool balance and accounting |
GET |
/insurance/premium-quote |
Risk-scaled premium quote |
| Agent identity (on-chain) | ||
POST |
/identity/register |
Register identity with on-chain deploy |
GET |
/identity/{agent_id} |
Look up on-chain agent identity |
POST |
/identity/delegate |
Delegate capability to sub-agent |
GET |
/identity/capabilities/{agent_id} |
Own + delegated capabilities |
| Identity registry (hosted) | ||
POST |
/identity-registry/register |
Register DID + stake + capabilities |
GET |
/identity-registry/{did} |
Look up by DID |
GET |
/identity-registry/by-account/{hash} |
Look up by account hash |
POST |
/identity-registry/{did}/reputation |
Update reputation counters |
POST |
/identity-registry/{did}/decay |
Apply time-based reputation decay |
POST |
/identity-registry/{did}/slash |
Slash stake for misbehaviour |
POST |
/identity-registry/{did}/verify |
Advance verification level |
POST |
/identity-registry/{did}/capabilities |
Update agent capabilities |
GET |
/identity-registry/search/agents |
Search agents by capability/score |
GET |
/identity-registry/stats/summary |
Registry-wide summary stats |
| Risk scoring | ||
GET |
/risk/score/{agent} |
IsolationForest agent risk score |
GET |
/risk/dashboard |
Aggregated risk dashboard |
| Admin (API-key gated) | ||
POST |
/admin/configure-fee |
Set insurance fee (basis points) |
POST |
/admin/set-release-cap |
Update arbiter-quorum release cap |
POST |
/admin/set-arbiters |
Rotate 5-account arbiter pool |
POST |
/admin/emergency-freeze |
Pause all state changes |
POST |
/admin/unfreeze |
Resume after freeze |
402 response format:
{
"error": "payment_required",
"accepts": "x402-v1",
"price": 1000000,
"receiver": "account-hash-74c9..."
}The real, live deployment (sandbox=false) requires a genuine Ed25519-signed
X-Payment header on every request β EscrowClient.generate(...) handles
that for you, deriving your agent's identity from a fresh keypair:
from sdk import EscrowClient
async with EscrowClient.generate("https://agentescrow402-api.onrender.com") as client:
receiver = "ab" * 32 # counterparty's 64-hex Casper account hash / Ed25519 public key
escrow = await client.create_escrow(receiver=receiver, amount=5_000_000, ttl=300)
await client.release(escrow["service_hash"])Running against your own local sandbox instance (SANDBOX=true) still works
with a plain string identity and no signing:
client = EscrowClient("http://localhost:8000", sender="my-agent", sandbox=True)See examples/quickstart.py (minimal) and examples/escrow_agent.py (full
autonomous buyer/seller lifecycle with a real dispute + AI arbitration call)
for runnable end-to-end examples. escrow_agent.py signs real Ed25519
arbiter votes and resolves the dispute on-chain out of the box, using the
throwaway, never-funded testnet keypairs committed under
demo/test-arbiter-keys/ (see that folder's README for why it's safe to
ship these particular private keys) β clone and run it, no credentials
needed, to watch create β dispute β resolve happen for real on Casper
Testnet.
from sdk.langchain_tool import EscrowPaymentTool
tool = EscrowPaymentTool(base_url="http://localhost:8000", sender="my-agent") # sandbox mode
result = await tool.run(action="create", receiver="ab" * 32, amount=1_000_000)python sdk/mcp_server.pyFull SDK reference β docs/SDK.md Β· Standalone JSON-Schema for all 26 tools (no server needed to browse) β docs/mcp_tools_schema.json
| Layer | Technology |
|---|---|
| Smart contract | Rust β WASM, Casper 2.x CEP-88 |
| Payment server | Python 3.11, FastAPI, Uvicorn |
| x402 middleware | Custom header parser + validator |
| Persistence | Neon PostgreSQL-compatible serverless database for hosted API records |
| Sandbox / testing | In-memory fallback for local/demo development |
| SDK | Python async SDK, LangChain tool, MCP server |
| CI | GitHub Actions β lint β pytest β WASM build β cargo test |
| Frontend | React + Vite console, Vercel |
| Backend hosting | Render (always-on) |
python -m compileall -q server
npm --prefix frontend run build
uv run --active python -m pytest -q # 450 tests (server logic, x402, identity, risk, multi-asset)
cargo test --manifest-path contracts/escrow/Cargo.toml # 40 tests (escrow, HTLC, arbitration)
ALLOW_HOSTED_DEMO_IDENTITY=true uv run python tests/test_business_logic.py # live smoke: health/stats/escrow create+release/risk/VRF/insuranceCurrent status: 450/450 Python + 40/40 Rust tests passing (incl. Hypothesis/proptest
property-based invariant tests added for fee/insurance/TTL/quorum/reputation logic). (One test,
test_delegate_expired_timestamp_rejected, has an occasional cross-module flake tied to
in-memory identity-registry state sharing between test files β not a production code bug, tracked
in docs/STATUS_AND_ROADMAP.md.) NVIDIA API-assisted security review
reported no concrete HIGH blockers for the latest console/Neon/contract patch.
| Flow | Deploy/tx hash | Result |
|---|---|---|
| Escrow create (CSPR) | a3c5da80...f6f40a8ad |
β processed |
release() on the current (v8, fixed) contract |
0184cc2b...06c3ee53 |
β processed, no error |
| HTLC atomic-swap upgrade deploy | 2211685a...29c7aaa2 |
β processed |
HTLC commit_swap β reveal_swap, revealed by a different account than the committer |
live round-trip on testnet, same session | β funds transferred (2.94 CSPR), cross-account reveal confirmed |
| CEP-18 token transfer (AETUSD) | 2139b49e...07a82c387 |
β
balance confirmed via state_get_dictionary_item |
| CEP-78 NFT mint (AETNFT) | 3a298259...f39d441 |
β
ownership confirmed via token_owners dict |
| CEP-78 NFT transfer (AETNFT) | c4046eaa...d599bc9d |
β
ownership confirmed via token_owners dict |
All hashes are independently verifiable on testnet.cspr.live or via
https://api.testnet.cspr.cloud/deploys/{hash}.
Bulk on-chain volume: beyond the curated flows above, 349/349 additional deploys were
submitted and confirmed on testnet with zero failures (same escrow contract as above) β
not just create_escrow spam, but the full escrow lifecycle: 173 create_escrow, 166
release, 4 sender-initiated refund, and 3 full dispute β 3-of-5 arbiter-multisig
resolve cycles (signed live with the same demo/test-arbiter-keys/ used by
examples/escrow_agent.py, confirming the arbiter set survived
the v8βv9 in-place contract upgrade). 20 of the 349 (10 create/release pairs) use the
10 pre-generated agent_01..agent_10 accounts as the counterparty receiver, so the log
also demonstrates multi-agent-wallet participation, not just a single sender/receiver pair.
Full deploy-hash-by-deploy-hash log:
docs/evidence/bulk_escrow_tx_log.jsonl.
| Doc | What's in it |
|---|---|
| docs/ARCHITECTURE.md | Detailed component/sequence diagrams |
| docs/SDK.md | Python SDK, LangChain tool, MCP server (26 tools) reference |
| docs/openapi.yaml | Full OpenAPI schema for the REST API |
| docs/GAS_BENCHMARK.md | Real testnet gas costs per escrow entry point |
| docs/AGENT_IDENTITY_REGISTRY.md | On-chain DID/stake/reputation registry contract (ID-1), separate from the escrow contracts |
| docs/STATUS_AND_ROADMAP.md | Architecture decisions, roadmap, and production status |
| ROADMAP.md | Shipped vs. planned, phase by phase |
| CHANGELOG.md | Version history |
| CONTRIBUTING.md | Dev setup, PR conventions | | SECURITY.md | Vulnerability disclosure, key-handling policy |
Security: testnet keys only β never commit real deployer keys. See docs/STATUS_AND_ROADMAP.md for architecture decisions and roadmap.
Built for Casper Agentic Buildathon 2026 Β· Deployed on Casper Testnet
ae402.xyz Β· API Docs Β· Architecture
Last verified against commit 5cdd4a8 / contract package d3ca33d1...c8eeb v9 (612cead2...ddd9ec), 2026-07-07.




