Conditional Settlement (v1)

Status: Draft v1 — 2026-05-19 Extension URI: https://projectmaya.deoscomputing.io/settlement/conditional/v1 Maintainer: DEOS Computing (did:web:deos.computing) Depends on: Receipt format v1, UCAN tokens v1 Companions: Receipt-Rooted Reputation white paper, a2a-ext/substrate-receipts/v1


1. Motivation

Agent-to-agent payments today move unconditionally: funds transfer when the action initiates, regardless of whether the outcome is acceptable. Disputes are post-hoc and live in a parallel evidence world (chargebacks, support tickets, arbitration) with no link back to the signed action history. The receipt-reputation white paper §1 calls this the aggregator problem; conditional settlement is the same problem applied to money.

Conditional settlement composes substrate receipts with payment rails so the release/refund decision is computed from receipt presence and absence, not negotiated out-of-band:

  1. An escrow receipt at action_can = settlement.conditional/escrow names the funds, the rail, the success condition (typically: an outcome receipt from a specific principal matching a pattern), the deadline, and the refund policy.
  2. The escrow holder (smart contract, payment processor, or substrate operator) reads the substrate and computes the current disposition: await | release | refund_timeout | refund_disputed.
  3. When the disposition transitions to a terminal state, the rail moves funds and the holder appends a settlement.conditional/settled receipt naming the disposition and the on-rail transaction reference.

A relying party — user, regulator, counterparty's customer — re-proves the payment-vs-outcome state purely from receipts. No chargeback portal, no out-of-band reconciliation.

This spec defines:

Rail-specific integrations are out of scope — those are operator deployments. §6 lists rails this spec considers but does not bundle.


2. Wire format

2.1 Escrow receipt

The action that creates a conditional payment. Issued by the principal authorizing the spend; signed by the substrate per the normal receipt flow.

{
  "id": "<blake3 of canonical body>",
  "principal": "did:deos:alice",
  "subject": "did:deos:agent-alice",
  "issued_at": 1716000000000000000,
  "capability": { "chain": [/* UCAN chain rooted at alice */] },
  "action": {
    "type": "escrow",
    "inputs_hash": "<...>",
    "metadata": {
      "rail": "ethereum/usdc",
      "rail_chain_id": 1,
      "rail_asset": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
      "amount": "87.99",
      "currency": "USDC",
      "from": "0xAlice...",
      "to": "0xBob...",
      "escrow_holder": "0xEscrow...",
      "memo": "amazon order B0XXX dog food",
      "condition": {
        "release_on": {
          "outcome_principal": "did:deos:bob-courier",
          "action_can_pattern": "shipment.delivered/v1",
          "metadata_match": { "to": "0xAlice..." }
        },
        "deadline_ns": 1716604800000000000,
        "refund_on_timeout": true,
        "refund_on_disputed": true,
        "dispute_principal": "did:deos:alice"
      }
    },
    "result_hash": null
  },
  "signatures": [/* substrate signature */]
}

action.action_can

MUST be settlement.conditional/escrow. This is what the escrow holder filters on when scanning the log.

metadata.rail

The payment rail identifier, free-form but SHOULD be one of:

Custom rails MUST namespace under their operator's URI prefix.

metadata.rail_chain_id, metadata.rail_asset

Required for EVM rails; identify the network + token contract. Other rails define their own analogues (fednow.routing, lightning.network).

metadata.amount, metadata.currency

Amount as a base-10 decimal string (NOT a float — exact). Currency is the ISO-4217 code or the asset symbol where ISO doesn't apply. currency SHOULD match rail's native denomination; conversions are out of scope.

metadata.escrow_holder

Address (or DID, depending on rail) of the party that holds funds during the conditional window. For on-chain rails this is typically a deployed escrow contract. For FedNow/RTP it's a regulated PSP. The principal MUST have ensured escrow_holder is operationally bound to honor this spec; this spec does not certify holders.

metadata.condition.release_on

Defines the success outcome.

Field Required Meaning
outcome_principal DID expected to issue the outcome receipt
action_can_pattern The outcome receipt's action_can must equal this (no wildcards in v1 — exact string match)
metadata_match optional Sub-object; every key must appear in the outcome receipt's action.metadata with the same scalar value

The outcome receipt MUST be issued at or after the escrow receipt's issued_at and strictly before deadline_ns.

metadata.condition.deadline_ns

Absolute deadline in unix nanoseconds. After this point the holder MAY transition to refund_timeout (per refund_on_timeout) without waiting for further outcome receipts.

metadata.condition.refund_on_timeout, metadata.condition.refund_on_disputed

Booleans. If both are false and the timeout / dispute occurs, the funds remain in escrow (a frozen disposition — see §3) until the dispute resolution flow ships in v1.1.

metadata.condition.dispute_principal

DID authorized to issue a settlement.conditional/dispute receipt that, if refund_on_disputed is true, terminates the escrow into refund_disputed. Typically the original principal (Alice) but MAY differ (a regulator, a guardian).

2.2 Outcome receipt (referenced, not defined here)

The outcome receipt is just an ordinary substrate receipt whose action_can matches the release_on.action_can_pattern. It's issued by outcome_principal per its own normal authorization. The escrow receipt does NOT need to know how the outcome is generated — it just specifies what to look for.

Example outcome shape (informative, not normative):

{
  "principal": "did:deos:bob-courier",
  "subject": "did:deos:agent-bob-courier",
  "action": {
    "type": "delivered",
    "metadata": {
      "to": "0xAlice...",
      "tracking_id": "1Z999...",
      "delivered_at": "2026-05-21T17:42:11Z"
    }
  }
}

action.metadata.to matches escrow.metadata.condition.release_on.metadata_match.to. That match is what lets the holder bind this outcome to this escrow, not another from the same courier.

2.3 Dispute receipt

An action receipt by dispute_principal at action_can = settlement.conditional/dispute. Its metadata MUST include the escrow receipt's id in metadata.escrow_receipt_id and SHOULD include reason (free-form string).

2.4 Settled receipt

Once the holder moves funds, it appends an action receipt at action_can = settlement.conditional/settled to its own substrate (typically the holder's substrate, not the principal's). Metadata names the escrow + disposition + rail-side transaction reference.

{
  "principal": "did:deos:escrow-holder",
  "subject": "did:deos:escrow-holder-agent",
  "action": {
    "type": "settled",
    "metadata": {
      "escrow_receipt_id": "<id of the original escrow receipt>",
      "disposition": "release",
      "rail": "ethereum/usdc",
      "rail_tx": "0xabcd...",
      "settled_at": "2026-05-22T01:13:00Z"
    }
  }
}

The settled receipt's existence is itself part of the audit chain — anyone (including the original principal) verifies fund movement against the rail and against this receipt.


3. Disposition algorithm

Given an escrow receipt and a substrate (or substrates, when the outcome principal uses a different substrate from the principal — see §5), the holder computes one of:

Disposition Meaning
await Outcome not yet observed, deadline not yet passed. Hold funds.
release Matching outcome receipt observed within deadline. Release funds to metadata.to.
refund_timeout now_ns >= deadline_ns and no matching outcome. If refund_on_timeout, refund to metadata.from; else frozen.
refund_disputed A dispute receipt by dispute_principal was observed before the matching outcome (or before the deadline). If refund_on_disputed, refund; else frozen.
frozen Terminal state reached but neither refund flag was true. Funds remain in escrow pending v1.1 resolution flow.

The MUST-implement algorithm (informative pseudocode):

def disposition(escrow, now_ns, substrate):
  e = escrow.metadata
  c = e.condition

  # Disputed beats outcome (unless outcome came first chronologically)
  outcome = substrate.find_outcome(escrow, e, c)  # see below
  dispute = substrate.find_dispute(escrow, c.dispute_principal)

  if dispute and (not outcome or dispute.issued_at_ns < outcome.issued_at_ns):
    return "refund_disputed" if c.refund_on_disputed else "frozen"

  if outcome:
    return "release"

  if now_ns >= c.deadline_ns:
    return "refund_timeout" if c.refund_on_timeout else "frozen"

  return "await"

def find_outcome(escrow, e, c):
  for r in substrate.iter_receipts_since(escrow.issued_at_ns):
    if r.principal != c.release_on.outcome_principal: continue
    if r.action.action_can != c.release_on.action_can_pattern: continue
    if r.issued_at_ns >= c.deadline_ns: continue
    if c.release_on.metadata_match:
      if not metadata_subset(c.release_on.metadata_match, r.action.metadata):
        continue
    return r
  return None

The algorithm is monotonic in time: a release at T is still release at T+1; same for refund states. Only await transitions out. Once the holder writes a settled receipt for any terminal state, the conditional is closed.


4. Holder obligations

A conforming escrow holder MUST:

  1. Verify the escrow receipt's substrate signature and capability chain before accepting funds. If the chain doesn't authorize the escrow action, reject without holding.
  2. Verify that the from address (or equivalent) is bound to the principal — either by capability metadata or by rail-side proof.
  3. Monitor the substrate for outcome and dispute receipts. Polling cadence is operator policy; SSE / webhooks recommended where the substrate supports them.
  4. On terminal disposition, move funds within an operator-published SLA. The SLA MUST be at least monotone: once the holder commits to a disposition, it does not reverse.
  5. Append the settled receipt to its own substrate before the user-visible funds-moved status flips. If the holder fails to settle within its SLA, the relying party can re-derive the disposition and pursue rail-side remedies; the absent settled receipt is itself evidence.
  6. Treat its own substrate signatures as binding — a holder who issues a contradictory settled receipt (two settled receipts for the same escrow with different dispositions) has self-doxxed and SHOULD be rejected by downstream parties.

A conforming holder MUST NOT:

  1. Release funds on an outcome receipt whose action_can doesn't exactly match release_on.action_can_pattern.
  2. Wait past deadline_ns + holder_grace_seconds (holder policy, typically ≤ 600s) before transitioning out of await.
  3. Issue a settled receipt for a disposition that isn't computed by the algorithm in §3.

5. Cross-substrate composition

The escrow receipt lives on the principal's substrate. The outcome receipt may live on a different substrate — typically the outcome principal's own substrate (e.g., Bob's courier substrate).

The holder MUST be able to read from both. Two acceptable patterns:

"release_on": {
  "outcome_principal": "did:deos:bob-courier",
  "action_can_pattern": "shipment.delivered/v1",
  "substrate_url": "https://substrate.bob-courier.example.com",
  "metadata_match": { "to": "0xAlice..." }
}

When the holder reads from a non-principal substrate, it MUST verify the outcome substrate's trust root and signatures independently. The principal's substrate's trust does not extend transitively.


6. Rail considerations (non-normative)

The spec is rail-agnostic. The following are the rails this design was sanity-checked against; conforming implementations on each are deferred to operator deployments.

6.1 EVM stablecoins (USDC / USDT / DAI)

Natural fit. escrow_holder is a deployed escrow contract whose code reads the substrate via an oracle (or via a trusted relayer that submits substrate inclusion proofs). Funds enter via ERC-20 transferFrom into the contract; release/refund are contract calls authorized by the relayer-signed disposition.

Practical concern: the substrate is off-chain. The escrow contract trusts whoever submits a "this disposition is final" proof. Reasonable v1 designs:

6.2 FedNow / RTP

US instant-payment rails. escrow_holder is a regulated PSP that holds funds during the conditional window. Disposition decisions are made off-rail (per §3) and translated into FedNow / RTP messages. Refund is a reverse credit transfer; release is a normal credit transfer.

Practical concern: FedNow and RTP have settlement finality measured in seconds, not hours, which is faster than typical conditional windows (delivery confirmations come in days). The PSP holds the float; the spec assumes the PSP is solvent and compliant with its regulator.

6.3 SEPA Instant

EU equivalent. Same shape as FedNow/RTP.

6.4 Lightning Bitcoin

escrow_holder is an HTLC counterparty with a custom preimage protocol: the preimage is revealed to the holder upon disposition computation, and the holder publishes settlement-as-onchain-tx or routes the preimage. Less smooth than EVM because Lightning's native conditionals are payment-hash-based, not application-state-based; the holder bridges.

6.5 Internal (substrate-operator-held)

Useful for substrate-internal economies — a substrate operator runs an L2-style balance system and conditional payments are just bookkeeping. No external rail. Suitable for closed-network deployments.


7. Composition with A2A and reputation

A conditional payment receipt flows through A2A Task artifacts like any other (per a2a-ext/substrate-receipts/v1). A counterparty requesting payment returns a Task=COMPLETED artifact whose receipt is at action_can = settlement.conditional/escrow. The relying party verifies the chain and MAY monitor the substrate for outcome/dispute progression.

For reputation, past escrow dispositions feed the outcomeRatio derivation directly: every escrow → release is a clean outcome, every escrow → refund_disputed is a disputed one. A counterparty with a high refund_disputed rate carries falsifiable bad reputation, signed and anchored.


8. Threat model

8.1 Holder corruption

The holder appends a settled receipt for release even though no outcome was observed. Defended by:

8.2 Outcome forgery

The outcome principal fakes a delivery receipt. Defended by:

8.3 Timing attacks (last-minute outcome)

The outcome principal posts a delivered receipt one second before deadline_ns but the delivery didn't actually happen. Defended only by:

8.4 Substrate downtime

The holder cannot read the principal's substrate at deadline. Conservative behavior: stay in await until reachable, then run the algorithm. SLA breach belongs to the substrate operator, not the holder. The holder MAY define a maximum-await ceiling and transition to frozen after it; this is operator policy and SHOULD be advertised.

8.5 Dispute spam

A misconfigured dispute_principal issues a dispute on every escrow, freezing all funds. Defended by:


9. Reference implementation

The TS SDK ships @deos/receipt/settlement exposing:

The TS verifier does not move funds — that is rail-specific. It produces the disposition a holder would act on.

The Maya substrate ships no holder code. A holder is whoever the principal contractually engaged; this spec defines what they have to honor.


10. Versioning

v1.0. The URI https://projectmaya.deoscomputing.io/settlement/conditional/v1 is immutable; breaking changes go to /v2.

Planned for v1.1:


11. Maintainer

DEOS Computing — pull requests, rail-integration prototypes, and threat-model critiques to github.com/DEOS-Computing.

License: CC BY 4.0 (text), Apache-2.0 (reference code).