A plain x402 payment settles the moment the facilitator verifies it — before the buyer has any way to know whether the seller actually delivered. If a provider takes payment and returns garbage, the settlement already happened, with no on-chain recourse. Nobody has solved this on Stellar yet. x402r exists for exactly this problem on EVM and Solana, but not here. Vellar's bond system is the gap it closes: a seller posts a bond, a buyer's settlement registers standing to dispute, and a bad delivery can be slashed on-chain instead of just disputed in someone's inbox.
Click any step to expand what actually happens on-chain, with the real proven transaction for each — this isn't a design description, it's a sequence that has genuinely run on Stellar testnet.
A seller who wants to offer dispute standing calls deposit on the bond-escrow contract, locking real testnet USDC against their resource key. This is what a buyer's later dispute can actually slash — no bond, no standing.
When a buyer pays a bonded seller through x402, the facilitator calls register_settlement on the bond-escrow contract as part of handling POST /settle — this is what gives the buyer standing to dispute later. The gating condition, exactly as wired: if (bondEscrow && result.success === true), awaited before /settle reports success back to the buyer. If bonding is configured and registration fails, the whole settle call returns 503 bond_registration_unavailable rather than silently succeeding without giving the payer dispute standing.
The call is signed by a dedicated admin key (BOND_ESCROW_ADMIN_SECRET_KEY) — structurally never the facilitator's payment-sponsor key. There is no code path in src/bond.ts that even reads the sponsor key.
Before any of this matters, the seller registers a delivery-signing public key with set_delivery_key. When they deliver the resource, they can sign a receipt with that key — proof, checkable by anyone, that delivery happened. Posting that receipt on-chain (post_receipt) is one of the entry points the contract exposes but no HTTP relay wires up yet (see Part 3 below) — today a seller would call it directly via CLI/SDK.
Every register_settlement call sets a claim_deadline on that settlement. If no dispute is filed before that deadline elapses, the seller can call withdraw and reclaim their bond — the buyer's window to dispute has simply passed with nothing filed.
The response window on this testnet deployment is 5 minutes — PLACEHOLDER_RESPONSE_WINDOW_SECONDS in contracts/bond-escrow/src/lib.rs:235, an explicit placeholder, not 24 hours. The design reasoning argues for something closer to 24h before any real/pubnet use; 5 minutes was chosen deliberately so the full lifecycle below could be exercised in real ledger time during testing.
If the buyer believes delivery failed (or never happened), they call file_dispute against the registered settlement. If the seller doesn't produce a valid receipt before the claim deadline, anyone can call finalize once the window elapses — the contract slashes the bond and pays the buyer.
This exact sequence has genuinely happened on this deployed contract: a dispute was filed, a real ~5m30s wait elapsed on the actual network clock with no receipt posted, and finalize executed a real SEP-41 transfer slashing 0.025 USDC from the bond to the payer.
register_settlement wired into POST /settle (PR #75, merged) — gated on bondEscrow && result.success === true, synchronous and awaited before /settle reports successdocs/proposal-provider-bond.md, 7 sections, lockedBOND_ESCROW_CONTRACT_ID is not configured on the hosted facilitator (vellar-facilitator.onrender.com) that this playground talks to. The wiring is real, merged, and correct in the code — but it is not currently active on the shared instance.deposit, withdraw, file_dispute, set_delivery_key, post_receipt, finalize) are callable only via direct Soroban CLI/SDK today, not through any facilitator HTTP route.bonded_only filter in the Bazaar catalog/discovery API.vellar-sdk receipt-signing helper.The contract is deployed and the payment path is wired. The full dispute flow requires the seller and buyer to interact with the contract directly via CLI today. The HTTP relay routes that make this seamless are next.
Reads a SettlementRecord straight from the deployed contract — no facilitator, no session, just a public read against Stellar testnet.
# Read a SettlementRecord directly from the deployed bond-escrow contract. # payment_id is 32 bytes, hex-encoded (64 hex chars) — same shape # src/bond.ts's registerSettlement expects. The value below is the real # register_settlement call's own tx hash, used illustratively: this # specific one was tested against the live contract and returned no # record (that proven sequence was run by direct CLI before any # payment ever produced a payment_id through the facilitator itself). # Swap in a payment_id from your own registered settlement to see it. stellar contract invoke \ --id CAWQ2FJDPWHOFLYQIPKBU4M6IE4GUROKUKVVZERWQVD2DHP7S2CULTI4 \ --source-account <any funded testnet account, read-only call> \ --rpc-url https://soroban-testnet.stellar.org \ --network-passphrase "Test SDF Network ; September 2015" \ --send=no \ -- get_settlement \ --payment_id 972d1379aca6b461efa7004cdeeb6aaef6ee4debbd9fd919c46ff61b3790ed8a