Settlement
Every tool invocation in chio has a cost. Settlement is the process of moving capital from the caller to the provider to pay for that cost. chio supports seven settlement rails from CapitalExecutionRailKind (Manual, Api, Ach, Wire, Ledger, Sandbox, Web3), a payment adapter abstraction for off-chain flows, and a full on-chain escrow system for trustless settlement via smart contracts.
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 | Test rail for development. Simulates settlement without moving real capital. |
Web3 | On-chain settlement. Supports EVM (Base / Arbitrum) via chio-settle/evm.rs, Solana (Ed25519 memo program) via chio-settle/solana.rs, and Chainlink CCIP cross-chain coordination via chio-settle/ccip.rs. |
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. This trait provides a four-method lifecycle for payment processing:
pub trait PaymentAdapter {
/// Pre-authorize a maximum amount before invocation begins
async fn authorize(&self, amount: Money, metadata: PaymentMetadata)
-> Result<PaymentAuthorization>;
/// Capture the actual cost after invocation completes
async fn capture(&self, auth: &PaymentAuthorization, amount: Money)
-> Result<CaptureReceipt>;
/// Release unused authorization (overage)
async fn release(&self, auth: &PaymentAuthorization)
-> Result<ReleaseReceipt>;
/// Refund a previously captured amount
async fn refund(&self, capture: &CaptureReceipt, amount: Money)
-> Result<RefundReceipt>;
}The authorization object tracks the state of each payment flow:
pub struct PaymentAuthorization {
pub authorization_id: String,
pub settled: bool,
pub metadata: PaymentMetadata,
}Pre-authorization flow
max_cost_per_invocation before the tool call, capture the actual cost after the call completes, then release the overage. If the actual cost is zero (for example, a cached result), the entire authorization is released with no capture.Here's the full authorize-capture-release cycle:
// 1. Pre-authorize the maximum possible cost
let auth = adapter.authorize(
max_cost_per_invocation,
PaymentMetadata { invocation_id, tool_id, .. }
).await?;
// 2. Execute the tool invocation
let result = invoke_tool(&request).await?;
// 3. Capture the actual cost
let actual_cost = result.metered_cost();
if actual_cost.is_zero() {
// No cost incurred, release the full authorization
adapter.release(&auth).await?;
} else {
adapter.capture(&auth, actual_cost).await?;
// Overage is automatically released on capture
}On-Chain Settlement (Web3 Rail)
The Web3 rail settles tool invocation costs on-chain using a suite of smart contracts. This provides trustless settlement where neither party needs to trust the other or a centralized intermediary.
Smart Contracts
The on-chain settlement system is composed of five 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. This is the fast path: settlement completes in a single transaction once both parties agree on 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. This is the trustless path: settlement does not require the operator's cooperation, only a valid proof against a root in
ChioRootRegistry.
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 | Full settlement complete, all 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.
Setting Up an Escrow
Here is a complete example of creating an escrow, executing a tool invocation, and settling via the DualSignature path:
use chio_settlement::{EscrowRequest, SettlementPath, DualSignatureRelease};
// 1. Create and fund the escrow
let escrow = chio_escrow.create_escrow(EscrowRequest {
caller: caller_address,
provider: provider_address,
token: usdc_address,
amount: max_cost,
timeout_seconds: 3600,
invocation_id: invocation_id.clone(),
}).await?;
// State: PendingDispatch -> EscrowLocked
// 2. Execute the tool invocation
let receipt = invoke_tool(&request).await?;
let actual_cost = receipt.metered_cost();
// 3. Operator signs the release for the actual cost
let release = DualSignatureRelease {
escrow_id: escrow.id,
amount: actual_cost,
receipt_hash: receipt.content_hash(),
};
let operator_sig = operator_key.sign(&release.signing_payload());
// 4. Provider submits the dual-signed release on-chain
chio_escrow.settle_dual_sig(release, operator_sig, provider_sig).await?;
// State: EscrowLocked -> SettledSettling via Merkle Proof
If the operator is unresponsive, the provider can settle using a merkle proof once the receipt has been included in a published checkpoint:
use chio_settlement::{MerkleProofRelease, MerkleInclusionProof};
// 1. Wait for the receipt to be checkpointed
let checkpoint = wait_for_checkpoint(&receipt.id).await?;
// 2. Generate the merkle inclusion proof
let proof = checkpoint.prove_inclusion(&receipt.content_hash())?;
// 3. Submit the proof to the escrow contract
let release = MerkleProofRelease {
escrow_id: escrow.id,
amount: actual_cost,
receipt_hash: receipt.content_hash(),
proof: MerkleInclusionProof {
leaf: receipt.content_hash(),
siblings: proof.siblings,
index: proof.index,
},
checkpoint_root: checkpoint.root,
};
chio_escrow.settle_merkle_proof(release).await?;
// State: EscrowLocked -> SettledCheckpoint Anchoring
Checkpoints are periodic commitments of receipt history to on-chain registries. The kernel publishes merkle roots to the ChioRootRegistry contract, creating an immutable anchor for the receipt log.
Checkpoint anchoring uses three lanes for defense in depth:
- 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
A complete proof bundle chains from the individual receipt all the way to the 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) |
Dispute windows vary by settlement path:
- DualSignature: no dispute window. Both parties signed, settlement is final on chain confirmation.
- MerkleProof: configurable challenge period (default 24 hours) during which the operator can dispute the proof before funds are released.
The full set of settlement lifecycle states covers both successful and exceptional flows:
pub enum SettlementState {
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. If a chain reorganization invalidates a settlement transaction, the escrow reverts to EscrowLocked and settlement must be retried. The kernel monitors for reorgs on all supported chains and automatically re-submits affected settlements.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 ChioPriceResolver contract provides verified exchange rates via Chainlink price feeds for the USDC/USD, ETH/USD, and BTC/USD pairs.
The price resolver applies several safety mechanisms:
- Staleness checks: prices older than a configured threshold (for example, 1 hour for major pairs) are rejected. The contract reverts if the feed's latest update is stale.
- L2 sequencer protection: on L2 chains (Optimism, Arbitrum, Base), the contract checks the Chainlink sequencer uptime feed. If the sequencer was recently down, a grace period is enforced before prices are considered valid.
- FX evidence: cross-currency settlements must include an
FxEvidencerecord with the rate, source, and timestamp. This evidence is stored on the receipt for auditability.
// Query the on-chain price resolver
let rate = chio_price_resolver.get_rate(
token_pair, // e.g., USDC/ETH
max_staleness, // e.g., 3600 seconds
).await?;
// Attach FX evidence to the settlement
let fx_evidence = FxEvidence {
rate: rate.price,
source: "chainlink".into(),
feed_address: rate.feed,
timestamp: rate.updated_at,
sequencer_status: rate.sequencer_ok,
};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 | Multi-lane proofs: EVM primary + Bitcoin OTS + Solana memo |
| Finality | L1Finalized, OptimisticL2, SolanaConfirmed with chain-specific rules |
| Price Oracle | ChioPriceResolver with Chainlink feeds, staleness checks, and L2 sequencer protection |
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