Soroban smart contract for settlement evidence, observer quorum, disputes, and finalization.
- Application Repository
- Application Documentation
- Application Releases
- Contract Release (v0.1.1)
- Active Testnet Contract
- StellarExpert Contract Explorer
- Security Policy (
SECURITY.md) - Deployment Guide (
DEPLOYMENT.md) - Contributing Guidelines (
CONTRIBUTING.md)
- Contract Purpose
- Protocol Architecture & Boundary
- State Machine
- Pure M-of-N Observer Quorum
- Dispute Expiration & Ledger-Based TTL
- Public Interface Reference
- Protocol Events
- Reconciliation Break Codes
- Testnet Deployment
- Build and Verification
- Security & Invariants
- StellarClear Application
- Maintainers & Support
- Contributing
- License
SettlementRegistry is the authoritative on-chain protocol layer for StellarClear on Stellar/Soroban.
The contract records and governs:
- Settlement Cases: Case creation, cryptographic terms commitments, and counterparties.
- Observations: Stellar transaction hashes, ledger sequence numbers, and observation commitments.
- Match / Break Decisions: Positive reconciliation matches or typed reconciliation breaks (
BreakCode). - Owner & Counterparty Attestations: Cryptographic commitments submitted by primary trade principals.
- Observer Attestations: Cryptographic evidence submitted by whitelisted observer addresses.
- Quorum Configuration: Per-case threshold configuration (
M-of-N). - Disputes: Formal dispute initiation on broken cases with ledger-based TTL.
- Resolution Commitments: Dual-party agreed terms resolving active disputes.
- Irreversible Finalization: Terminal locking of matched or resolved settlement outcomes.
Non-Custodial Design: The smart contract does NOT custody user funds, escrow balances, or execute asset transfers. Value transfers occur on underlying Stellar payment rails.
┌─────────────────────────────────────────────────────────┐
│ Off-Chain App (stellarclear-app) │
│ - Private settlement documents & canonicalization │
│ - SHA-256 commitment generation (terms, obs, res) │
│ - Stellar ledger indexing & automated break detection │
└──────────────────────────┬──────────────────────────────┘
│ Anchors State & Proofs
▼
┌─────────────────────────────────────────────────────────┐
│ Soroban Smart Contract (This Repo) │
│ SettlementRegistry Contract │
│ - Case Lifecycle (Open -> Observed -> Matched/Break...) │
│ - Observer Authorization & Evidence Records │
│ - Dispute Opening & Two-Party Resolution Agreement │
│ - Pure M-of-N Quorum & Irreversible Finalization │
└─────────────────────────────────────────────────────────┘
Settlement cases follow an explicit, deterministic state progression:
Open ──► Observed ──┬──► Matched ─────────────────────────────────► Finalized
│
└──► Break ──► Disputed ──► Resolved ─────────► Finalized
▲ │
└──(TTL)───┘ (expire_dispute)
-
Happy Path (Direct Reconciliation Match):
Open -> Observed -> Matched -> FinalizedOpen -> Observed: Triggered byrecord_observationfrom an authorized observer.Observed -> Matched: Triggered byrecord_matchfrom an authorized observer.Matched -> Finalized: Triggered byfinalize_casefrom the case owner once Owner attestation and the required M-of-N observer quorum attestations are recorded.
-
Dispute & Mutual Resolution Path:
Observed -> Break -> Disputed -> Resolved -> FinalizedObserved -> Break: Triggered byrecord_breakwith a typedBreakCodefrom an authorized observer.Break -> Disputed: Triggered byopen_disputeoropen_dispute_with_ttlfrom the case owner or counterparty.Disputed -> Resolved: Triggered bysubmit_resolutionwhen both owner and counterparty independently submit identical 32-byte resolution commitments prior to dispute TTL expiration.Resolved -> Finalized: Triggered byfinalize_casefrom the case owner once Owner, Counterparty, and M-of-N observer quorum attestations are recorded.
-
Dispute Expiration / Timeout Path:
Disputed -> (TTL Expiry) -> BreakDisputed -> Break: Triggered by permissionlessexpire_disputeonce the current ledger sequence reaches or exceedsdispute_expires_at_ledger.- Critical Invariant: A dispute timeout never transitions to
Resolvedand never fabricates mutual agreement. It resets the case toBreak, allowing parties to renegotiate or open a new dispute.
The protocol implements a pure M-of-N observer quorum model:
-
Any M Distinct Valid Observers: For a case requiring quorum threshold
$M$ , any$M$ distinct valid observer attestations satisfy the quorum. -
Original Observer Not Mandatory: The observer that submitted the initial
record_observationhas no special finalization privilege and is not mandatory for finalization. - Role Separation: Case Owner and Counterparty cannot submit observer attestations or count toward observer quorum.
- Deduplication: Duplicate attestation submissions from the same observer count as a single attestation slot.
-
Historical Attestation Validity: Observer authorization is evaluated at submission time (
submit_attestationorsubmit_observer_attestation). Once recorded, an attestation represents immutable historical evidence and remains valid even if the observer is subsequently revoked by the admin. -
Default Threshold: Cases default to
quorum = 1for backwards compatibility.
MAX_OBSERVER_QUORUM = 10: Maximum allowed observer quorum threshold per case.MAX_OBSERVERS_PER_CASE = 16: Maximum distinct observer attestations recorded per case.
Dispute lifetimes are tracked via deterministic Stellar ledger sequence numbers, avoiding dependency on non-deterministic wall-clock timestamps:
- Default Expiration:
DEFAULT_DISPUTE_TTL_LEDGERS = 17_280ledgers (~24 hours at 5 seconds/ledger) applied byopen_dispute. - Custom Expiration: Configurable via
open_dispute_with_ttl(initiator, case_id, dispute_commitment, ttl_ledgers). - TTL Bounds:
MIN_DISPUTE_TTL_LEDGERS = 120ledgers (~10 minutes).MAX_DISPUTE_TTL_LEDGERS = 518_400ledgers (~30 days).
- Deterministic Timeout: If
current_ledger >= dispute_expires_at_ledgerwithout matching resolution submissions,expire_disputetransitions the case deterministically fromDisputed -> Break, clears pending resolution commitments, and emitsDisputeExpired.
The SettlementRegistry contract exposes 22 public functions grouped by operational domain:
| Function | Description | Callable By |
|---|---|---|
__constructor(admin: Address) |
One-time initialization setting the protocol admin and version 1. |
Deployer |
add_observer(observer: Address) |
Registers an authorized settlement observer address. | Contract Admin |
remove_observer(observer: Address) |
Revokes authorization for an existing settlement observer. | Contract Admin |
is_observer(observer: Address) -> bool |
Checks whether an address is currently an authorized observer. | Public (Read-only) |
| Function | Description | Callable By |
|---|---|---|
create_case(case_id, owner, counterparty, terms_commitment, expires_at_ledger) |
Opens a new settlement case (defaults quorum to 1). | Case Owner |
set_case_quorum(case_id, quorum) |
Configures observer quorum threshold (1 <= quorum <= 10). |
Case Owner |
get_case_quorum(case_id) -> u32 |
Reads configured observer quorum threshold for a case. | Public (Read-only) |
| Function | Description | Callable By |
|---|---|---|
record_observation(observer, case_id, tx_hash, observed_ledger, observation_commitment) |
Anchors Stellar transaction evidence on open case. | Registered Observer |
record_match(observer, case_id) |
Records positive reconciliation match (Observed -> Matched). |
Registered Observer |
record_break(observer, case_id, break_code) |
Records reconciliation break with standardized BreakCode (Observed -> Break). |
Registered Observer |
| Function | Description | Callable By |
|---|---|---|
submit_attestation(case_id, role, commitment) |
Submits 32-byte cryptographic attestation (Owner, Counterparty, Observer). |
Authorized Principal |
submit_observer_attestation(case_id, observer, commitment) |
Submits authorized observer attestation towards M-of-N quorum. | Registered Observer |
get_attested_observers(case_id) -> Vec<Address> |
Queries distinct observer addresses that submitted attestations for a case. | Public (Read-only) |
get_attestation(case_id, party) -> Option<Attestation> |
Queries registered attestation for a specific address. | Public (Read-only) |
| Function | Description | Callable By |
|---|---|---|
open_dispute(initiator, case_id, dispute_commitment) |
Opens dispute with default ~24h TTL on broken case. | Case Owner / Counterparty |
open_dispute_with_ttl(initiator, case_id, dispute_commitment, ttl_ledgers) |
Opens dispute with custom TTL ledgers (120..=518,400). |
Case Owner / Counterparty |
expire_dispute(case_id) |
Permissionless timeout: resets unaddressed dispute back to Break. |
Anyone (Permissionless) |
get_dispute_expiration(case_id) -> Option<u32> |
Reads expiration ledger sequence for an active dispute. | Public (Read-only) |
| Function | Description | Callable By |
|---|---|---|
submit_resolution(party, case_id, resolution_commitment) |
Submits resolution terms; transitions to Resolved once both match. |
Case Owner / Counterparty |
finalize_case(case_id) |
Irreversibly locks settlement outcome after verifying required role and quorum attestations. | Case Owner |
| Function | Description | Callable By |
|---|---|---|
get_case(case_id) -> SettlementCase |
Queries complete on-chain case record. | Public (Read-only) |
get_resolution(case_id, party) -> Option<BytesN<32>> |
Queries submitted dispute resolution commitment for a specific address. | Public (Read-only) |
The contract emits typed Soroban events for all state mutations. Downstream indexers in StellarClear/stellarclear-app ingest these events to maintain real-time off-chain database synchronization:
| Event Name | Topics | Data Payload | Firing Entrypoint |
|---|---|---|---|
ObserverAdded |
observer: Address |
() |
add_observer |
ObserverRemoved |
observer: Address |
() |
remove_observer |
CaseCreated |
case_id: BytesN<32> |
owner, counterparty, expires_at_ledger |
create_case |
CaseQuorumSet |
case_id: BytesN<32> |
quorum: u32 |
set_case_quorum |
ObservationRecorded |
case_id: BytesN<32> |
observer, tx_hash, observed_ledger |
record_observation |
CaseMatched |
case_id: BytesN<32> |
observer |
record_match |
CaseBroken |
case_id: BytesN<32> |
observer, break_code |
record_break |
AttestationSubmitted |
case_id: BytesN<32> |
attestor, role |
submit_attestation / submit_observer_attestation |
DisputeOpened |
case_id: BytesN<32> |
initiator, dispute_commitment |
open_dispute / open_dispute_with_ttl |
ResolutionSubmitted |
case_id: BytesN<32> |
resolver, resolution_commitment |
submit_resolution |
DisputeResolved |
case_id: BytesN<32> |
resolution_commitment |
submit_resolution |
DisputeExpired |
case_id: BytesN<32> |
expiration_ledger, closed_at_ledger |
expire_dispute |
CaseFinalized |
case_id: BytesN<32> |
finalized_at_ledger |
finalize_case |
When payment reconciliation fails, registered observers classify discrepancies using typed BreakCode enum variants:
| Break Code | Meaning |
|---|---|
AmountMismatch |
Observed payment amount differs from settlement terms. |
AssetMismatch |
Asset/token delivered differs from agreed currency or issuer. |
DestinationMismatch |
Transaction paid an address other than the agreed recipient. |
ReferenceMismatch |
Payment memo, invoice ID, or reference tag does not match instructions. |
MissingSettlement |
No matching payment transaction was observed on the ledger before expiration. |
DuplicateSettlement |
Multiple conflicting payment transactions observed for the same case. |
LateSettlement |
Payment occurred after the agreed expiration ledger sequence. |
FailedTransaction |
Observed Stellar transaction failed or reverted on-chain. |
UnexpectedTransaction |
Unrequested or unauthorized payment attributed to case reference. |
| Parameter | Value |
|---|---|
| Network | Stellar Testnet |
| Contract ID | CCPCMPIUTKBLSJVSGPPSUHTBBY6HHT3OTE3CKUXC5BD2B3YEDSAROGC5 |
| Contract Explorer | StellarExpert Testnet Explorer |
| Release Tag | v0.1.1 |
| Git Commit | 2032666be97e8bf09ba9073952041ae3e4573ff1 |
| WASM SHA-256 | 0073a4cb2027140ac34e4db6c64c2d4104590ec60cf0ef2e424909eba9ae36ac |
| WASM Size | 32,773 bytes (optimized, reproducible) |
- Release Tag:
v0.1.0 - WASM SHA-256:
1018a81b1ac95046cb00466ceda7ee347204c08b71b1c51b3c9611dd32215d66 - Contract ID:
CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC
Note
The historical prototype (1018a81b...) corresponds strictly to the initial v0.1.0 milestone. The active, hardened protocol deployment is v0.1.1 (0073a4cb...).
- Rust:
stable(1.84.0+MSRV) - Target:
wasm32v1-none - Stellar CLI:
28.1.0+ - Soroban SDK:
27.0.4+
# 1. Check code formatting
cargo fmt --all -- --check
# 2. Run clippy linter with warnings treated as errors
cargo clippy --workspace --all-targets --all-features -- -D warnings
# 3. Run all workspace unit, invariant, and integration tests
cargo test --workspace
# 4. Build optimized contract WASM binary
stellar contract build
# 5. Verify byte-for-byte reproducible artifact integrity
./scripts/verify-artifact.sh
# 6. Execute full quality check suite
./scripts/check.shPlease review our complete Security Policy (SECURITY.md).
- Unaudited Smart Contract: The contract is a reference protocol implementation and has not yet undergone an independent third-party security audit. Do not use in production without independent professional auditing.
- Non-Custodial: The contract holds no user funds, token balances, or custody escrows.
- On-Chain Authorization: Strict role-based authentication (
require_auth(),require_admin_auth(),require_observer_auth()) enforced at runtime. - Terminal Immutability: Finalized cases permanently reject all mutating calls (
Error::InvalidState). No administrative override or backdoors exist.
The smart contract is the trust-minimized anchor of the larger StellarClear ecosystem:
stellarclear-contract(This Repo): Soroban protocol layer governing settlement state, observer quorum, attestations, disputes, and finalization.stellarclear-app(Companion Repo): Comprehensive off-chain platform providing client SDKs, REST API, ingestion indexers, matching engine, proof generators, and database persistence.- Repository:
StellarClear/stellarclear-app - Documentation: https://stellarclear.github.io/stellarclear-app/
- Releases: Application Releases
- Repository:
| Name | GitHub | Role |
|---|---|---|
| Adejumo | @Adejumo-2 | Lead Developer & Maintainer |
| Smog | @smog123 | Core Contributor & Maintainer |
- GitHub Discussions: Ask questions and participate
- GitHub Issues: Report bugs or suggest features
We welcome contributions from the community. Please review our Contributing Guidelines (CONTRIBUTING.md) and Code of Conduct (CODE_OF_CONDUCT.md) before submitting pull requests.
Licensed under the Apache License, Version 2.0. See LICENSE for details.
