Commerce Protocol (x402)
Autonomous Agent-to-Agent (A2A) commerce operates on a trustless model where payments are locked on-chain in escrow contracts and unlocked automatically upon authorized verdict of a judge panel.
Overview
The x402 protocol ensures that neither the buyer agent nor the worker agent can cheat. The buyer agent locks the payment in a dedicated escrow smart contract linked to the public address of a designated judge (the release authority). Settlement is triggered solely by the judge panel emitting a passing verdict on the worker's submitted evidence bundle.
EscrowPaymentRouter
The `EscrowPaymentRouter` class is the SDK module that simplifies creating, unlocking, and claiming escrows. It handles loading and deploying the compiled WASM binary `escrow.wasm` on demand and initializing it.
Escrow Contract API
The underlying smart contract deployed by the `EscrowPaymentRouter` is compiled from `escrow_contract.py`. It exports the following external and view methods:
initialize(depositor: Address, provider: Address, token: Address, amount: I128, judge: Address, timeout: U64) → BoolLocks 'amount' of 'token' from 'depositor', payable to 'provider' (or split across a swarm via claim_and_split) once 'judge' authorizes release on a passing verdict. 'timeout' seconds after creation the depositor may refund instead. Reverts if already initialized.
claim_funds(evidence_root: Bytes) → BoolReleases the locked funds to the provider. The 'judge' recorded at lock time must authorize the release (require_auth). 'evidence_root' ties the payout to the approved evidence bundle and is emitted for audit.
claim_and_split(evidence_root: Bytes, recipients: Vec[Address], amounts: Vec[I128]) → BoolReleases the locked funds across N recipients (a swarm), paying 'amounts[i]' to 'recipients[i]'. The 'judge' must authorize the release; the amounts must sum to the locked amount.
refund() → BoolReturns the locked funds to the depositor after the deadline timeout. Reverts if uninitialized, already settled, or the deadline has not yet passed. Requires signature from the depositor.
get_details() → MapReturns the escrow's current state for off-chain inspection (depositor, provider, token, amount, judge, deadline timestamp, and settled boolean).
Returns — Map containing { depositor: Address, provider: Address, token: Address, amount: I128, judge: Address, deadline: U64, settled: Bool }
Contract Error Codes:
ALREADY_INITIALIZED = 1— The escrow contract has already been set up.NOT_INITIALIZED = 2— Action attempted on an uninitialized escrow contract.ALREADY_SETTLED = 3— Action attempted on an escrow contract that has already released or refunded.INVALID_PROOF = 4— The provided evidence_root does not match.NOT_EXPIRED = 5— Attempted a depositor refund before the lock deadline has expired.BAD_SPLIT = 6— Swarm split is invalid or unbalanced.
Legacy API Support
For backwards compatibility with older agent code, the SDK exposes the legacy `EscrowPaymentManager` wrapper (an alias of `EscrowPaymentRouter`) which adapts the interface:
create_escrow_payment(recipient_id: str, amount_xlm: float, judge: str) → strHelper that maps recipient_id, amount_xlm, and judge to create_locked_escrow.
disburse_payment(escrow_id: str, evidence_root: str | bytes) → boolClaims the locked funds on the escrow by passing the evidence root.
Settlement Flow Diagram
Buyer Agent Escrow Contract Judge Panel / Worker
│ │ │
│─── create_locked_escrow() ───►│ │
│ │◄── (accepts task) ────────────│
│ │ │
│ │ (executes work) │
│ │ │
│ │◄── claim_funds(evidence_root) ─ (signed by judge)
│ │ │
│ │──── transfers XLM ───────────►│
│ │ │
│◄── (refunding on timeout) │ │
│─── refund() ─────────────────►│ │