← Changelog

Initial escrow + lot lifecycle

v0.1.0 · January 30, 2026 · Contract

The first public release of the FairFoundry Fabric. Three roles, one escrow, one lot lifecycle. Everything later releases ship — staking, dispute resolution, governance timelock, the per-service Service Fabric — extends the primitives that landed here.

Three roles, one contract

Every contract instance is initialized with three addresses, each with non-overlapping authority:

RoleAuthority
OEMDeposits escrow, withdraws unused escrow, executes settlement
FactoryCreates lots against escrowed funds; receives payment on approval
QACommits test metadata, signs off on lot, updates final unit counts

The role separation is enforced by Soroban's require_auth() primitive. Every state-changing function is gated on a specific caller; cross-role grants are not possible without re-initialization — and re-initialization itself was hardened in v0.4.1.

Escrow

Escrow is a single OEM-controlled balance held inside the contract. The OEM funds it ahead of production; the contract pays it out as lots get approved.

pub fn deposit_escrow(env: Env, from: Address, amount: i128)
pub fn withdraw_escrow(env: Env, oem: Address, amount: i128)

Withdrawal is constrained by min_escrow_lots — the OEM cannot drain escrow if it would leave the balance below the floor needed to cover currently-open lots. This is the contract's solvency guarantee: a factory creating a lot today knows the funds to settle it are committed and cannot be pulled mid-flight.

Lot lifecycle

A “lot” is a batch of manufactured units. The factory creates it; the QA tests it; the contract pays for it. The whole journey is one state machine.

State diagram

Open
  ↓  qa_commit (QA submits test metadata)
InQA
  ↓  qa_update_counts (QA finalizes pass/fail counts)
Approved
  ↓  execute_payment (payment > 0)         ↓  execute_payment (payment ≤ 0)
Paid                                          Closed

Disputed and Refunded were reserved as LotStatus variants in v0.1.0 but not exercised — they're the entry points consumed by the dispute path that lands in v0.3.0.

Mutators

FunctionCallerTransition
create_lotFactory(none) → Open
qa_commitQAOpen / InQAInQA
qa_commit_serialsQAAdds per-unit serial-number commitments
qa_commit_attestationQAAdds testbench attestation (equipment identity)
qa_commit_fullQACombined metadata + serials + attestation in one call
qa_update_countsQAInQAApproved (when all units accounted for)
execute_paymentOEMApprovedPaid or Closed

Init parameters

The contract is configured at init; every parameter that matters for the lifecycle is set then. The relevant subset for v0.1.0:

ParameterTypePurpose
oem / factory / qaAddressThe three roles, all required to authorize init
pay_assetPaymentAssetWhich token settles payouts (Stellar native, USDC, etc.)
pricingPricingprice_per_unit + per-failure penalties
ersERSHash of the Engineering Requirement Spec the lot is built against
min_escrow_lotsu32Solvency floor; escrow cannot drop below this many open lots
min_qa_stakei128Reserved for v0.2.0 staking; set to 0 in v0.1.0 deployments
oem_bondi128Reserved future field
dispute_window_secsu64Floor: 1800 seconds (30 minutes); used by the v0.3.0 dispute path

Both oem.require_auth() and factory.require_auth() and qa.require_auth() must succeed for init to land. No role can deploy a contract that binds the others without their on-chain consent.

Tests

30 tests pass. Coverage:

  • Init happy paths (Cloud-hosted + Customer-hosted deployment models)
  • Init parameter validation (negative price, negative stake, undersized dispute window)
  • Escrow deposit / withdraw / solvency floor enforcement
  • Lot create / qa_commit / qa_commit_full / qa_update_counts / execute_payment happy paths
  • Status-machine violations (qa_commit on a Paid lot, execute_payment from non-Approved)
  • Authorization violations (factory tries to deposit, OEM tries to qa_commit, etc.)
  • Payment math: per-unit price minus failure penalties; Closed path when penalties exceed lot value

Compiles cleanly for wasm32-unknown-unknown. Full suite runs in well under a second.

What's not here yet

  • QA collateral → v0.2.0 adds stake_qa + slashing primitives
  • Disputes → v0.3.0 adds re-inspection + governance timelock
  • Per-service settlement → v0.4.0 adds the Service Fabric on top of the lot pipeline

References

Source: github.com/fairb-dev/Fairfoundry. Architecture diagrams + the full contract API table live on the platform page. The technical narrative on what the contract actually settles is in the blog post The smart contract behind a 48-hour settlement.

Want to read the contract?

Two contracts, ~3000 lines of Rust, 61 tests, CC BY-NC 4.0.