The smart contract behind a 48-hour settlement
The marketing line is "verified work settles in 48 hours, not 12 weeks." This post is about what's actually running on-chain to make that true. Two Stellar Soroban contracts, six core mutating functions, a five-stage lifecycle, and a 48-hour OEM objection window encoded directly in the state machine. Source: github.com/fairb-dev/Fairfoundry.
The shape
Two contracts, three roles, five lifecycle stages.
The settlement contract holds OEM escrow, tracks each manufacturing service through the lifecycle, takes a split platform fee (0.375% from each side, 0.75% total Cloud), and releases payment when the verification oracle attests completion. The SVT contract issues soulbound (non-transferable) Service Validation Tokens — portable on-chain quality credentials.
Three roles are set once at init: OEM (buyer), Factory (manufacturing partner), QA (quality assurance staking collateral). A fourth role, the FairBuild oracle, is registered separately via register_fairbuild_oracle.
Five lifecycle stages are first-class citizens in the type system:
pub enum ServiceStage {
EVT, // Engineering Validation Test
DVT, // Design Validation Test
PVT, // Production Validation Test
MP, // Mass Production
Sustaining, // Ongoing production + maintenance
}
Each stage carries the same on-chain mechanics — escrow, attestation, settlement — but the SVTs minted at each stage compose a permanent quality record specific to that stage of the program.
The state machine
A service order moves through five states. Each transition is one contract call:
The OrderStatus enum encodes the same machine. Cancelled is the only off-path destination, reachable only from Requested (pre-accept). Once the factory has accepted, the order can only proceed through validation or land in dispute.
Why 48 hours, not "instant"
When the oracle attests at attest_completion, the order moves to Validated. Settlement does not auto-fire. There's a 48-hour OEM objection window. If the OEM raises a dispute via request_reinspect within that window, escrow freezes and the dispute path takes over.
If 48 hours pass with no objection, anyone can call settle_service and the contract releases funds to the factory. The "any caller" property is intentional — it means a factory's automation script, an OEM's relayer, or a third-party operator can all trigger settlement once the SVT exists. The factory always receives the funds at their own address; the caller is just the gas-payer.
One caveat shipped recently: settling as credits instead of direct payment now requires factory authorization. Credit settlement routes funds to an on-ledger ledger entry the factory must later redeem. That's the factory's economic choice; pre-fix, any third party could have forced it.
pub fn settle_service(env: Env, order_id: String, as_credit: bool) -> i128 {
// ...status checks...
// Credit-mode settlement requires factory auth.
// Direct payment is callable by anyone (relayer-friendly).
if as_credit {
order.factory.require_auth();
}
// ...fee deduction + transfer...
}
Disputes, and the 96-hour bound
If the OEM objects within the 48-hour window, escrow freezes. The challenge mechanism is bounded by design — both sides have skin in the game and the resolution time is encoded in the contract. The QA stakes collateral via stake_qa; their stake locks during a challenge and slashes on a missed-deadline default.
Two recent guard fixes shipped here too: late QA responses now revert (now > due_by → TooEarlyOrLate) so missed-deadline penalties can't be silently bypassed by a late response that flips the challenge to Responded. And challenge_default_slash now rejects slash_bps == 0 and refuses to accept the QA itself as the caller — closing a path where the very party being slashed could clear their own default.
What's tested
61 tests pass in 0.15 seconds. They cover happy paths (direct payment, credit settlement, multi-stage progression EVT → MP), negatives (auth failures, state violations, escrow underflows, fee-rate validation, role mismatches), and properties (escrow conservation, SVT monotonicity, accumulation/redemption invariants).
What they don't yet cover, and we know it: try-call negative tests for the new auth guards, late post-deadline reinspection responses, artifact-root mismatches between factory submission and oracle attestation, and re-init attempts. Test-coverage hardening is its own backlog item.
What's not on-chain
The marketing copy says "verified by an automated layer." On-chain, that means the oracle attests that it verified. The actual ML/metrology pipeline that turns 10,000 MTF measurements into a pass/fail call runs off-chain — at FairBuild, today. The oracle's attestation is a signed claim, not a proof.
This is the trust assumption the system depends on. For a centralized pilot where FairBuild operates the oracle itself, it's acceptable and arguably preferable (lower latency, fewer infra dependencies, clear accountability). For decentralized claims later, it becomes a multi-oracle quorum or a cryptographic-proof problem. We've documented this as a roadmap item rather than a "done" checkbox.
Why Soroban
EVM was the obvious choice; we didn't pick it. The reasons fit in a separate post — short version: built-in soulbound primitives, first-class fiat-asset rails, deterministic replay, and a smaller blast radius for the kind of contract that's gated by an off-chain oracle anyway. Coming next on this blog.
Jisoo Lee is CEO of Fairbuild. Source: github.com/fairb-dev/Fairfoundry.