Chio/Docs
LOGIN · JOIN

BuildWeb3

Settlement

Review supported settlement networks, release paths, and receipts that link authorized capabilities to on-chain payments.

Looking for a walkthrough?

For a guide to wiring settlement into an agent deployment (network selection, payment adapters, escrow setup, reconciliation), see the Settlement guide. This page focuses on what each integrated chain supports and how the on-chain artifacts map back to chio's receipts.

Supported Chains

Settlement is handled by the chio-settle crate and its contract bindings. An operator pins one settlement chain config: contract addresses, operator address, settlement token, and a tiered confirmation and dispute policy. The ChioEscrow and ChioBondVault contracts are EVM-generic, so any EVM chain with them deployed can be the pinned target; Base is the shipped reference. Separate Solana support verifies Ed25519 receipts locally and prepares a canonical release payload; it does not broadcast on-chain.

ChainAssetsFinalityStatus
Base (EVM, L2)USDC · ETHOptimisticL2 · L1FinalizedShipped reference (pinned chain)
Other EVM chainsPer pinned configOptimisticL2 · L1FinalizedPinnable (one active config)
SolanaEd25519 receiptsSolanaConfirmedLocal verification and instruction prep only

Solana support verifies locally; it does not settle on mainnet

Solana support is bounded to local verification and canonical instruction preparation, not live broadcast or indexing. It verifies Chio Ed25519 receipts and key bindings locally and emits a canonical chio.settle.solana-release.v1 payload for commitment-parity checks. It does not provide the EVM escrow and bond path.

Architecture

Every settlement starts as a chio.web3-settlement-dispatch.v2 artifact. The settle crate composes it from a signed capital execution instruction, the pinned chain config and a signed identity binding, derives the escrow id from the contract itself, and validates the result before anything is submitted (crates/economy/chio-settle/src/evm/prepare.rs:150-240). It then submits and watches for finality. Once finality is reached a chio.web3-settlement-execution-receipt.v2 is produced (chio-settle/src/evm/finalize.rs, observe.rs:213). Both artifacts travel inside a SignedExportEnvelope, whose signature is over the canonical JSON of the body (crates/core/chio-core-types/src/receipt/lineage.rs:410-431), so any client that canonicalizes the same way verifies the same bytes.

rendering
The kernel never talks to a chain: it authorises, and the settle runtime composes, submits and observes. The escrow terms hash the capability id, so an on-chain escrow is reachable from the capability that authorised it.

Authority boundary: receipts over chain state

Chio settlement authority comes from signed Chio receipts and pinned verifier policy. Chain evidence is subordinate observation: it can corroborate a claim, but it never overrides one. Chain data is evidence, readback, or observation unless the signed Chio verifier report also accepts the bound claim. On-chain state is never the authority in its own right.

Smart Contracts

On EVM, two contracts implement settlement. Both are deployed per-chain with stable addresses published in the chio-web3 trust profile. Alloy-generated Rust bindings live in the chio-web3-bindings crate.

  • IChioEscrow · opens a fixed-amount, time-bounded escrow that either a dual-signature release or a merkle-proof release can unlock. An escrow id is derived from its terms, and opening one that already exists reverts with EscrowAlreadyExists (contracts/src/ChioEscrow.sol:309), so a settled escrow is never reopened. Releases against a live escrow can be partial and accumulate: each one adds to released and emits EscrowPartialRelease until the deposit is drawn down (ChioEscrow.sol:452-467).
  • IChioBondVault · holds operator bonds that back dispute outcomes. On a slash verdict the bond is transferred to the disputing party minus a protocol fee; on a timeout refund the bond returns to the operator.

Solana support is not a live settlement program. It verifies Chio Ed25519 receipts and key bindings locally, prepares the canonical instruction set, and emits a chio.settle.solana-release.v1 payload for commitment-parity checks. There is no broadcast to Solana mainnet and no on-chain program verification in the shipped API.


Release Paths

An escrow unlocks in one of two ways. Both paths produce settlement receipts; the deployment selects one through policy decision, not a protocol one.

Dual SignatureMerkle Proof
Who signsCounterparty + Chio operatorAnyone, once the receipt batch is anchored
Trust modelMutual signature at release timeTrust the Merkle root; verify inclusion
Best forLow-volume, high-value transfersHigh-volume batched settlement
CostTwo signatures per escrowOne anchor tx amortised across receipts

Settlement Receipt

A settlement execution receipt is its own artifact, not a kernel receipt with different fields. Its Rust type is Web3SettlementExecutionReceiptArtifact in chio-web3, and it carries #[serde(deny_unknown_fields)], so a key the type does not declare is a parse error rather than an ignored extra. It embeds the whole dispatch it settles, which is what lets an auditor reconstruct the transaction from the receipt alone.

crates/economy/chio-web3/src/settlement.rs132-155rust
pub struct Web3SettlementExecutionReceiptArtifact {
    pub schema: String,
    pub execution_receipt_id: String,
    pub issued_at: u64,
    pub dispatch: Web3SettlementDispatchArtifact,
    pub observed_execution: crate::credit::CapitalExecutionObservation,
    pub lifecycle_state: Web3SettlementLifecycleState,
    pub settlement_reference: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub reconciled_anchor_proof: Option<AnchorInclusionProof>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub identity_registry_evidence: Option<Web3SettlementIdentityRegistryEvidence>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub identity_registry_evidence_binding: Option<Web3SettlementIdentityRegistryEvidenceBinding>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub oracle_evidence: Option<OracleConversionEvidence>,
    pub settled_amount: MonetaryAmount,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub reversal_of: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub failure_reason: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub note: Option<String>,
}

Eight of those fields are required by spec/schemas/chio-web3/v2/settlement-execution-receipt.schema.json: schema, execution_receipt_id, issued_at, dispatch, observed_execution, lifecycle_state, settlement_reference, and settled_amount. The rest are optional and omitted when absent. lifecycle_state is one of nine values: pending_dispatch, escrow_locked, partially_settled, settled, reversed, charged_back, timed_out, failed, and reorged.

A settled one ships in the public-settlement proof room fixture. Its own fields first, with the embedded dispatch held back:

settlement-receipt · receipttranscript
$ jq '.settlement_receipt | {schema, execution_receipt_id, issued_at, lifecycle_state,
$       settlement_reference, settled_amount, observed_execution}' \
    fixtures/proof-room/public-settlement/valid-offline-finality/settlement-proof-bundle.json
{
  "schema": "chio.web3-settlement-execution-receipt.v2",
  "execution_receipt_id": "receipt-web3-1",
  "issued_at": 1743292860,
  "lifecycle_state": "settled",
  "settlement_reference": "settlement-web3-1",
  "settled_amount": {
    "currency": "USD",
    "units": 150
  },
  "observed_execution": {
    "amount": {
      "currency": "USD",
      "units": 150
    },
    "externalReferenceId": "0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
    "observedAt": 1743292860
  }
}
exit 0

The chain, the escrow, the settlement token, and the release path live on the embedded dispatch rather than on the receipt. This is the same object with capital_instruction (the signed authority chain) removed for length:

settlement-receipt · dispatchtranscript
$ jq '.settlement_receipt.dispatch | del(.capital_instruction)' \
    fixtures/proof-room/public-settlement/valid-offline-finality/settlement-proof-bundle.json
{
  "beneficiary_address": "0x2222222222222222222222222222222222222222",
  "bond_vault_contract": "0x1000000000000000000000000000000000000003",
  "chain_id": "eip155:8453",
  "contract_package_id": "chio.official-web3-contracts",
  "dispatch_id": "dispatch-web3-1",
  "escrow_contract": "0x1000000000000000000000000000000000000002",
  "escrow_id": "escrow-web3-1",
  "issued_at": 1743292800,
  "note": "Dispatches one governed escrow release over the official Base-first contract stack.",
  "operator_key_hash": "0x0791868d8f29ea735f26a17a9aea038cd4255baac26eac5a74e58a07ed2f1975",
  "schema": "chio.web3-settlement-dispatch.v2",
  "settlement_amount": {
    "currency": "USD",
    "units": 150
  },
  "settlement_path": "merkle_proof",
  "settlement_token_address": "0x735F1Ba389D9D350501dB8FBbB5b52477DcaddA8",
  "support_boundary": {
    "anchor_proof_required": true,
    "custody_boundary_explicit": true,
    "oracle_evidence_required_for_fx": true,
    "real_dispatch_supported": true,
    "reversal_supported": true
  },
  "trust_profile_id": "chio.official-web3-stack"
}
exit 0

The full set of keys on the fixture receipt, so nothing above reads as the whole object:

settlement-receipt · keystranscript
$ jq '.settlement_receipt | keys' \
    fixtures/proof-room/public-settlement/valid-offline-finality/settlement-proof-bundle.json
[
  "dispatch",
  "execution_receipt_id",
  "issued_at",
  "lifecycle_state",
  "note",
  "observed_execution",
  "oracle_evidence",
  "reconciled_anchor_proof",
  "schema",
  "settled_amount",
  "settlement_reference"
]
exit 0

Not a receipt id

execution_receipt_id is a settlement identifier chosen by the settle runtime, not a Chio receipt id. Chio receipt ids are the 64-hex content address of the receipt body (crates/core/chio-core-types/src/receipt/body.rs:240-243); the two never interchange.

The Solana lane produces no artifact of this shape. prepare_solana_settlement returns a PreparedSolanaSettlement (crates/economy/chio-settle/src/solana.rs:45-63), which carries the program and mint addresses, the Ed25519 program id, signature and Chio public key, and the hex-encoded chio.settle.solana-release.v1 instruction payload. The binding runs the other way from the EVM lane: the payload commits to the Chio receipt through receiptHash, the sha256 of the canonical receipt body (solana.rs:177-191), and commitment() derives a SettlementCommitment from it for parity checks. Nothing is broadcast, so there is no transaction hash, no block number, and no inclusion proof to record.

Reading Receipts for Reconciliation

The route below returns kernel receipts, the ChioReceipt objects carrying the cost metadata a reconciliation joins on. It does not return the web3 execution-receipt artifact above. Receipts are read over GET /v1/receipts/query with a bearer token, so reconciliation runs in whatever language the finance side already uses. ReceiptQueryClient ships in both SDKs with the same filter names, all optional: capabilityId, toolServer, toolName, outcome, since, until, minCost, maxCost, costCurrency, agentSubject, cursor, and limit. The response carries totalCount, receipts, and a nextCursor when more remain.

import { ReceiptQueryClient } from "@chio-protocol/sdk";

const receipts = new ReceiptQueryClient(
  process.env.CHIO_CONTROL_URL!,
  process.env.CHIO_AUTH_TOKEN!,
);

const page = await receipts.query({
  capabilityId: "cap-expense-line-item-019",
  costCurrency: "USDC",
  limit: 10,
});

console.log(page.totalCount, page.receipts.length);

For a full reconciliation run, use paginate instead of query. It yields one page of receipts at a time and refuses to loop: a nextCursor that fails to advance raises rather than replaying the same page forever. The TypeScript form is an async generator; the Python form is an iterator.


Finality Modes

Settlement finality is a policy input, not a constant. An agent paying for a second of API time does not need L1-finalised settlement and should not wait 15 minutes for it. An agent releasing a bond after a dispute must wait. The chain-level finality mode comes from chio-web3's Web3FinalityMode, which has three variants:

ModeMeaningTypical latency
OptimisticL2Inclusion on the L2 sequencer~2 s (Base)
L1FinalizedIncluded in an L1-finalised block~13 min (Ethereum)
SolanaConfirmedConfirmed by a supermajority of stake~1 s

The mode above describes what a chain considers final. What settlement observation actually reports is separate and chain-agnostic: inspect_finality returns a SettlementFinalityStatus of AwaitingConfirmations, AwaitingDisputeWindow, Finalized, or Reorged. A Reorged observation drives the recovery path: ResubmitAfterReorg and the rest of the SettlementRecoveryAction set.


Dispute Policies

The dispatch envelope includes dispute policy. Each settlement declares the policy that will apply if either party raises a claim before the release window closes.

  • Off-chain arbitration · A designated arbiter address can sign a resolution that either party can execute on-chain. Lowest cost, highest trust assumption.
  • Timeout refund · If no counterparty signature arrives before the window closes, the escrow refunds to the depositor. No arbiter required.
  • Bond slash · If the operator breaches the agreement, the disputing party can trigger a bond slash against IChioBondVault. The protocol fee is deducted and the remainder transfers to the disputing party.

Bitcoin Anchoring

For the longest-lived audit guarantees, Chio publishes a super-root of its receipt Merkle tree to Bitcoin via OpenTimestamps and mirrors the same root to an EVM ChioRootRegistry. The chio-anchor crate is responsible for this process; it fails closed for each configured anchor, so a checkpoint is only considered anchored if EVM publication, Bitcoin attestation, and (where configured) a Solana memo record all succeed.

Why two anchors

EVM anchoring is fast enough to be verified on-demand during disputes. Bitcoin anchoring is slow but approaches nation-state-resistant immutability. Running both gives callers a choice between latency and durability at verification time without forcing either trade-off at publication time.

Next Steps

  • Settlement guide · walkthrough of payment adapters, escrow lifecycle, and oracle verification
  • Chainlink Oracles · price feeds and FX conversion that back cross-currency settlement
  • x402 Payments · per-request stablecoin payments at the HTTP layer
  • Receipt format · canonical JSON-LD shape shared by every receipt