BuildEconomics
Settlement
Settlement transfers payment from the caller to the provider for a tool invocation, using payment adapters or on-chain escrow.
Settlement rails
A settlement rail is the mechanism used to move capital between counterparties. Each rail is represented by a CapitalExecutionRailKind variant in the chio kernel:
| Rail | Description |
|---|---|
Manual | Human-initiated transfers outside the system (invoices, checks). Settlement is recorded manually. |
Api | Payment processor integrations (Stripe, Adyen). Pre-authorization and capture via the PaymentAdapter trait. |
Ach | ACH bank transfers. Lower cost, higher latency. Suitable for batch settlement. |
Wire | Wire transfers for high-value settlements with same-day finality. |
Ledger | Internal ledger entries between accounts within the same operator. No external capital movement. |
Sandbox | Development rail that simulates settlement without moving funds. |
Web3 | On-chain settlement via the chio-settle crate. Supports EVM (Base / Arbitrum) escrow, the bounded Solana Ed25519 memo path, and Chainlink CCIP cross-chain coordination. |
The rail is selected per-invocation based on the operator's configuration, the counterparty's supported rails, and the settlement amount. Most operators start with Sandbox during development and graduate to Api or Web3 in production.
Payment Adapter Integration
For off-chain rails (Api, Ach, Wire), settlement flows through the PaymentAdapter trait. The trait is synchronous and provides a four-method lifecycle for payment processing:
pub trait PaymentAdapter: Send + Sync {
/// Authorize or prepay up to `amount_units` before the tool executes.
fn authorize(
&self,
request: &PaymentAuthorizeRequest,
) -> Result<PaymentAuthorization, PaymentError>;
/// Finalize payment for the actual cost after tool execution.
fn capture(
&self,
authorization_id: &str,
amount_units: u64,
currency: &str,
reference: &str,
) -> Result<PaymentResult, PaymentError>;
/// Release an unused authorization hold.
fn release(
&self,
authorization_id: &str,
reference: &str,
) -> Result<PaymentResult, PaymentError>;
/// Refund a previously executed payment.
fn refund(
&self,
transaction_id: &str,
amount_units: u64,
currency: &str,
reference: &str,
) -> Result<PaymentResult, PaymentError>;
}The API uses request arguments instead of a Money type. The authorization object tracks the state of each payment flow, and capture, release, and refund all return one shared PaymentResult:
pub struct PaymentAuthorization {
pub authorization_id: String,
pub settled: bool,
pub metadata: serde_json::Value,
}
pub struct PaymentResult {
pub transaction_id: String,
pub settlement_status: RailSettlementStatus,
pub metadata: serde_json::Value,
}Pre-authorization flow
max_cost_per_invocation before the tool call, then capture the actual cost after the call completes. If the actual cost is zero (for example, a cached result), release the authorization instead of capturing it.Here is the authorize-capture-release cycle:
// 1. Pre-authorize the maximum possible cost.
let auth = adapter.authorize(&PaymentAuthorizeRequest {
amount_units: max_cost_units,
currency: "USD".to_string(),
payer: agent_id.clone(),
payee: tool_server_id.clone(),
reference: invocation_id.clone(),
governed: None,
commerce: None,
})?;
// 2. Execute the tool invocation.
let result = invoke_tool(&request)?;
// 3. Capture the actual cost, or release the whole hold if nothing was spent.
let actual_units = result.metered_cost_units();
if actual_units == 0 {
adapter.release(&auth.authorization_id, &invocation_id)?;
} else {
adapter.capture(&auth.authorization_id, actual_units, "USD", &invocation_id)?;
}On-Chain Settlement (Web3 Rail)
The Web3 rail settles tool invocation costs on-chain through smart contracts. A Merkle proof lets a provider release escrow without the operator or an intermediary.
Smart Contracts
The on-chain settlement system uses these contracts:
| Contract | Role |
|---|---|
ChioEscrow | Holds funds in escrow during tool invocations. Supports ERC-20 tokens with permit. |
ChioBondVault | Manages collateral bonds for credit facilities and autonomous agent execution. |
ChioRootRegistry | Stores merkle roots published by the kernel for checkpoint anchoring. |
ChioIdentityRegistry | Maps did:chio identifiers to on-chain addresses for settlement routing. |
ChioPriceResolver | Integrates oracle price feeds for cross-currency settlement and FX verification. |
Settlement Paths
Once funds are locked in ChioEscrow, they can be released through two settlement paths:
- DualSignature (two-party signed): the operator signs a release message authorizing the provider to withdraw. Settlement completes in one transaction after both parties sign the amount.
- MerkleProof(inclusion-proof from the receipt log): the provider submits a merkle proof showing that the invocation receipt was included in a published checkpoint. The provider submits a valid proof against a root in
ChioRootRegistry; the operator does not need to cooperate.
Choosing a settlement path
Escrow Lifecycle
The lifecycle flows PendingDispatch -> EscrowLocked -> PartiallySettled / Settled / Reversed / ChargedBack / TimedOut:
| State | Meaning |
|---|---|
PendingDispatch | Escrow created but funds not yet locked on-chain |
EscrowLocked | Funds locked in the ChioEscrow contract, invocation can proceed |
PartiallySettled | Some funds released to the provider, remainder still held |
Settled | All escrowed funds distributed |
Reversed | Escrow reversed, funds returned to the caller |
ChargedBack | Dispute resolved in caller's favor after settlement |
TimedOut | Escrow expired without settlement, funds returned to caller |
ERC-20 token support uses the EIP-2612 permit pattern, allowing callers to approve and deposit in a single transaction without a separate approval step.
Payment-Rail Compatibility
Beyond escrow and bond settlement, the chio-settle crate exposes support for established on-chain payment methods, so a settlement can use an existing method instead of the native escrow contracts. Each integration provides preparation and verification functions in the crate's payments module:
- x402 payment requirements (
build_x402_payment_requirements,X402SettlementMode) - EIP-3009
transferWithAuthorizationdigests (prepare_transfer_with_authorization,Eip3009Domain) - Circle nanopayments (
evaluate_circle_nanopayment,CircleNanopaymentPolicy) - ERC-4337 paymaster-sponsorship checks (
prepare_paymaster_compatibility,Erc4337PaymasterPolicy)
Setting Up an Escrow
Settlement uses Rust and SDK APIs
chio-settle crate's prepare / submit / confirm sequence. The economic layer exposes these as Rust and SDK APIs; there is no chio settle <chain> subcommand beyond chio settle status.Create an escrow and settle it through DualSignature as follows. The SDK prepares each call, validates the capital instruction and identity binding, then submits and confirms it:
use chio_settle::evm::{
prepare_web3_escrow_dispatch, submit_call, confirm_transaction,
finalize_escrow_dispatch, EscrowDispatchRequest,
};
use chio_core::web3::trust_profile::Web3SettlementPath;
// `config` (SettlementChainConfig), the signed capital instruction, and the
// operator's SignedWeb3IdentityBinding are assembled upstream.
let request = EscrowDispatchRequest {
dispatch_id: dispatch_id.clone(),
issued_at,
trust_profile_id: profile_id.clone(),
contract_package_id: contract_package_id.clone(),
capability_id: capability_id.clone(),
depositor_address: caller_address.clone(),
beneficiary_address: provider_address.clone(),
capital_instruction: instruction.clone(),
settlement_path: Web3SettlementPath::DualSignature,
oracle_evidence_required_for_fx: false,
note: None,
};
// 1. Prepare the escrow-create call. This validates the instruction and
// identity binding and derives the escrow id via a static contract read.
let prepared = prepare_web3_escrow_dispatch(&config, &request, &operator_binding).await?;
// State: PendingDispatch -> (submit) -> EscrowLocked
// 2. Submit and confirm the create transaction.
let tx_hash = submit_call(&config, &prepared.call).await?;
let tx_receipt = confirm_transaction(&config, &tx_hash).await?;
// 3. Finalize the on-chain escrow id from the emitted event.
let dispatch = finalize_escrow_dispatch(&prepared, &tx_receipt)?.dispatch;After the tool invocation produces a signed ChioReceipt, the operator prepares the dual-signature release and submits it. The release is configured to release the entire escrowed amount:
use chio_settle::evm::{
prepare_dual_sign_release, submit_call, confirm_transaction, DualSignReleaseInput,
};
let receipt = invoke_tool(&request).await?;
// 4. Prepare the dual-signature release for the observed amount.
let release = prepare_dual_sign_release(
&config,
&dispatch,
&receipt,
&DualSignReleaseInput {
operator_private_key_hex: operator_settlement_key_hex.clone(),
observed_amount: settlement_amount,
},
).await?;
// 5. Submit and confirm the release transaction.
let tx_hash = submit_call(&config, &release.call).await?;
confirm_transaction(&config, &tx_hash).await?;
// State: EscrowLocked -> SettledSettling via Merkle Proof
If the operator is unresponsive, the provider settles with an anchor inclusion proof once the receipt has been included in a published checkpoint. The dispatch must have been created with Web3SettlementPath::MerkleProof:
use chio_settle::evm::{
prepare_merkle_release, submit_call, confirm_transaction, EscrowExecutionAmount,
};
use chio_core::web3::anchors::AnchorInclusionProof;
// 1. Build the anchor inclusion proof once the receipt's checkpoint root is
// published to ChioRootRegistry (see chio-anchor).
let anchor_proof: AnchorInclusionProof = build_anchor_inclusion_proof(/* ... */)?;
// 2. Prepare the Merkle release, binding the anchor proof to the dispatch.
let release = prepare_merkle_release(
&config,
&dispatch,
&anchor_proof,
&anchor_content, // SettlementAnchorContentBinding
EscrowExecutionAmount::Full,
)?;
// 3. Submit and confirm the release transaction.
let tx_hash = submit_call(&config, &release.call).await?;
confirm_transaction(&config, &tx_hash).await?;
// State: EscrowLocked -> SettledCheckpoint Anchoring
Checkpoints commit receipt history to on-chain registries. The chio-anchor crate publishes Merkle roots to ChioRootRegistry and verifies returned proofs against those roots.
Checkpoint anchoring can use three independent methods, modeled as AnchorLaneKind::{EvmPrimary, BitcoinOts, SolanaMemo} and verified together as an AnchorProofBundle:
- EVM primary: merkle root published to
ChioRootRegistryon an EVM chain (Ethereum L1 or L2) - Bitcoin OpenTimestamps: OpenTimestamps proof anchored to the Bitcoin blockchain for calendar-independent timestamping
- Solana memo: checkpoint hash written as a Solana memo transaction for fast, low-cost redundancy
chio-anchor also owns a second, independent mechanism: chio.anchor_batch.v1, which Merkle-batches checkpoint IDs and binds the batch to a public witness (Rekor or OpenTimestamps) through a WitnessPolicy. A bounded Chainlink Functions request (ChainlinkFunctionsTarget) provides a fallback-verification path over a receipt batch.
A proof bundle links an individual receipt to a chain anchor:
Receipt (content hash)
-> Merkle inclusion proof (siblings + index)
-> Checkpoint statement (root + timestamp + lane)
-> Chain anchor (tx hash on EVM / BTC / Solana)Verification is offline-capable
Settlement Finality
Settlement finality depends on the chain where the escrow or checkpoint is anchored. Each chain has its own finality semantics:
| Finality Level | Chain | Meaning |
|---|---|---|
L1Finalized | Ethereum mainnet | Transaction included in a finalized epoch (~12 minutes) |
OptimisticL2 | Optimism, Arbitrum, Base | Soft-confirmed on L2, subject to challenge window for full finality |
SolanaConfirmed | Solana | Confirmed by supermajority of validators (~400ms) |
A dispute window is not an on-chain release gate. Both release paths in ChioEscrow execute immediately: releaseWithSignature settles the moment a valid dual signature lands, and releaseWithProof settles the moment a valid inclusion proof lands. Neither path carries a timelock. Dispute windows live one layer up, in two places, and neither is keyed on the settlement path.
The chio-settle observer selects a finality dispute window from the settlement amount. The default SettlementPolicyConfig tiers a dispute_window_secs across four bands of minor units:
| Settlement amount (minor units) | dispute_window_secs |
|---|---|
| up to 1,000 | 0 (immediate) |
| up to 100,000 | 3,600 (1 hour) |
| up to 1,000,000 | 14,400 (4 hours) |
| above 1,000,000 | 86,400 (24 hours) |
inspect_finality_for_receipt applies this tiering identically whether the release used DualSignature or MerkleProof. It classifies a SettlementFinalityStatus (AwaitingConfirmations, AwaitingDisputeWindow, Finalized, Reorged) after the release; it does not block the release call.
Separately, a Web3TrustProfile declares a per-path dispute_windows vector. Each entry pairs a settlement_path with challenge_window_secs, recovery_window_secs, and a dispute_policy (OffChainArbitration, TimeoutRefund, or BondSlash). validate_web3_trust_profile rejects a zero challenge or recovery window for any path, including DualSignature, so a compliant profile cannot declare a zero-duration window for the fast path.
Web3SettlementLifecycleState defines states for successful and exceptional flows. The type is in chio-web3, re-exported via chio_core::web3:
pub enum Web3SettlementLifecycleState {
PendingDispatch, // Created, awaiting on-chain funding
EscrowLocked, // Funded, invocation in progress
PartiallySettled, // Partial release completed
Settled, // Fully settled
Reversed, // Funds returned before settlement
ChargedBack, // Dispute resolved post-settlement
TimedOut, // Escrow expired without action
Failed, // Settlement transaction failed
Reorged, // Chain reorganization invalidated settlement
}Reorg handling
Reorged state is rare but important. Reorg handling is bounded, reviewable automation, not unconditional silent resubmission. The chio-settle observer classifies a reorg as SettlementFinalityStatus::Reorged and recommends SettlementRecoveryAction::ResubmitAfterReorg. Execution runs through a cron/log-triggered SettlementWatchdogJob that carries an operator_override_required flag; automation outcomes explicitly include ManualOverrideRequired.Oracle Price Verification
When tool costs are denominated in one currency but settlement occurs in another (for example, costs in USD, settlement in USDC or ETH), the chio-link crate resolves verified exchange rates. Its ChioLinkOracle implements the PriceOracle trait and reads prices from a pluggable OracleBackend that supports both Chainlink (AggregatorV3Interface.latestRoundData) and Pyth (Hermes). The on-chain ChioPriceResolver contract is a separate, contract-level price read used on L2; the receipt-side oracle evidence is produced by chio-link.
The oracle applies several safety mechanisms:
- Staleness checks: prices older than a configured threshold are rejected; the resolver denies conversion when the feed is stale.
- L2 sequencer protection: on L2 chains (Optimism, Arbitrum, Base), it checks the Chainlink sequencer uptime feed and fails closed while the sequencer is down or inside its post-recovery grace period.
- Cross-source divergence: the primary backend read is cross-checked against a fallback with a basis-point circuit breaker, failing closed on divergence. Per-pair
twap_enabledpolicy averages a rolling observation window into a TWAP. - Oracle evidence: cross-currency conversions produce an
OracleConversionEvidencerecord that is stored on the receipt for auditability.
// ChioLinkOracle implements PriceOracle: it resolves the rate from Chainlink
// and/or Pyth behind a divergence circuit breaker and an L2 sequencer check.
let rate = oracle.get_rate(&pair)?;
// The resolved rate converts into OracleConversionEvidence for the receipt.
let evidence = rate.to_conversion_evidence(/* conversion context */);
// evidence fields: schema, base, quote, authority, rate_numerator,
// rate_denominator, source, feed_address, updated_at, max_age_seconds,
// cache_age_seconds, converted_cost_units, original_cost_units,
// original_currency, grant_currency, oracle_public_key, signature.Summary
| Concept | Description |
|---|---|
| Settlement Rails | Seven rails (Manual, Api, Ach, Wire, Ledger, Sandbox, Web3) as CapitalExecutionRailKind variants |
| PaymentAdapter | authorize, capture, release, refund lifecycle for off-chain rails |
| On-Chain Escrow | ChioEscrow contract with DualSignature (fast) and MerkleProof (trustless) settlement paths |
| Checkpoint Anchoring | chio-anchor multi-lane proofs (EVM primary + Bitcoin OTS + Solana memo) plus the chio.anchor_batch.v1 witness mechanism |
| Finality | L1Finalized, OptimisticL2, SolanaConfirmed with chain-specific rules |
| Price Oracle | chio-link (ChioLinkOracle) with Chainlink + Pyth backends, divergence circuit breaker, staleness and L2 sequencer checks |
Next Steps
- Credit & Underwriting · credit facilities, bonds, and underwriting decisions
- Budgets & Metering · cost limits and usage tracking before settlement
- Receipts · the audit trail that settlement proves payment for