Skip to main content
Program ID: AhaDyVm8LBxpUwFdArA37LnHvNx6cNWe3KAiy8zGqhHF x0-escrow enables trustless conditional payments between agents and services. Funds are locked in a PDA-controlled token account until delivery conditions are met, a dispute is resolved, or a timeout expires.

State Machine

Every escrow follows a strict state transition graph:
Created ──fund──▶ Funded ──deliver──▶ Delivered ──release──▶ Released
   │                │                     │
   │cancel          │dispute              │dispute
   ▼                ▼                     ▼
Cancelled        Disputed ──resolve──▶ Released / Refunded

                    │timeout

                 Refunded

Funded ──timeout──▶ auto-release ──▶ Released (to seller)
StateDescription
CreatedEscrow account initialized, not yet funded
FundedBuyer has deposited tokens into the escrow vault
DeliveredSeller has marked delivery with optional proof hash
DisputedEither party has raised a dispute with evidence
ReleasedFunds released to seller (terminal)
RefundedFunds returned to buyer (terminal)
CancelledEscrow cancelled before funding (terminal)

Instructions

create_escrow

Creates a new escrow PDA and associated token vault.
create_escrow(
    amount: u64,
    memo_hash: [u8; 32],
    timeout_seconds: i64,
    arbiter: Option<Pubkey>
)
ParameterTypeDescription
amountu64Token amount to escrow (micro-units)
memo_hash[u8; 32]SHA-256 of the transaction memo/agreement
timeout_secondsi64Auto-release timeout (1 hour – 30 days)
arbiterOption<Pubkey>Optional third-party dispute resolver

fund_escrow

Buyer deposits the specified amount into the escrow vault. Transitions state from CreatedFunded.

mark_delivered

Seller claims delivery, optionally providing a proof hash. Transitions FundedDelivered.
mark_delivered(proof_hash: Option<[u8; 32]>)

release_funds

Buyer confirms satisfaction and releases funds to seller. Transitions DeliveredReleased. Also records a successful transaction to x0-reputation via CPI.

initiate_dispute

Either buyer or seller raises a dispute with evidence.
initiate_dispute(evidence_hash: [u8; 32])
Transitions to Disputed. Records the dispute_initiated_slot for arbiter delay enforcement.

resolve_dispute

Only callable by the designated arbiter. Subject to ARBITER_RESOLUTION_DELAY_SLOTS = 216,000 (~24 hours) — the arbiter cannot resolve immediately after a dispute is raised.
resolve_dispute(release_to_seller: bool)
  • true → Funds released to seller, state → Released
  • false → Funds refunded to buyer, state → Refunded

claim_auto_release

Seller claims funds after the timeout has expired. Only valid when state is Funded or Delivered and the timeout has passed.

claim_timeout_refund

Buyer claims a refund if the escrow has timed out without delivery and no auto-release was triggered.

cancel_escrow

Cancels the escrow before it is funded. Only the buyer can cancel. State → Cancelled.

State Account

EscrowAccount

pub struct EscrowAccount {
    pub version: u8,
    pub buyer: Pubkey,
    pub seller: Pubkey,
    pub arbiter: Option<Pubkey>,
    pub amount: u64,
    pub memo_hash: [u8; 32],
    pub state: EscrowState,
    pub timeout: i64,
    pub created_at: i64,
    pub delivery_proof: Option<[u8; 32]>,
    pub dispute_evidence: Option<[u8; 32]>,
    pub mint: Pubkey,
    pub token_decimals: u8,
    pub dispute_initiated_slot: u64,   // For arbiter delay
    pub bump: u8,
}
PDA Seeds: ["escrow", buyer, seller, memo_hash]

Timeout Mechanics

  • Default timeout: 72 hours (DEFAULT_ESCROW_TIMEOUT_SECONDS = 259,200)
  • Minimum: 1 hour (MIN_ESCROW_TIMEOUT_SECONDS = 3,600)
  • Maximum: 30 days (MAX_ESCROW_TIMEOUT_SECONDS = 2,592,000)
  • Buffer: An additional ESCROW_TIMEOUT_BUFFER_SLOTS = 1,500 (~10 minutes) is added as safety margin
  • Auto-release: After timeout, the seller can call claim_auto_release to receive funds

Reputation Integration

On escrow completion, x0-escrow updates x0-reputation via CPI:
OutcomeReputation Record
release_funds (buyer satisfied)record_success(response_time_ms)
resolve_dispute(true) (seller wins)record_resolution_favor()
resolve_dispute(false) (buyer wins)record_dispute()
Policy rejection during fundingrecord_failure(error_code)

Events Emitted

EventWhen
EscrowCreatedNew escrow initialized
EscrowFundedBuyer deposited tokens
DeliveryMarkedSeller claimed delivery
DisputeInitiatedDispute raised
FundsReleasedFunds sent to seller
FundsRefundedFunds returned to buyer
DisputeResolvedArbiter resolved dispute
Last modified on February 8, 2026