Status: Accepted
Date: 2026-03-28
Refs: escrow/src/lib.rs — InvoiceEscrow, DataKey::Escrow, fund_impl, settle, withdraw
The escrow needs a clear, auditable lifecycle so that state-changing entrypoints can enforce valid transitions and indexers can reconstruct history from events alone.
Use a single u32 status field on InvoiceEscrow with five values:
| Value | Name | Meaning |
|---|---|---|
0 |
open | Accepting investor funding |
1 |
funded | funded_amount >= funding_target; SME may withdraw or settle |
2 |
settled | SME called settle; investors may claim payout |
3 |
withdrawn | SME called withdraw; terminal, no settlement possible |
4 |
cancelled | Admin called cancel_funding; terminal, investors may refund |
Transitions are strictly forward (0 → 1 → 2, 0 → 1 → 3, or 0 → 4). No entrypoint moves status backward. The full escrow snapshot is stored under DataKey::Escrow and rewritten atomically on every state change.
- Any entrypoint that reads
statusgets a consistent view within a single host function call (Soroban single-writer model). settleandwithdrawboth requirestatus == 1, so they are mutually exclusive terminal paths.fundis blocked oncestatus != 0, preventing post-funded contributions.cancel_fundingis blocked oncestatus != 0, preventing cancellation of funded escrows.refundis only permitted whenstatus == 4, allowing principal recovery for cancelled escrows.- Property test
prop_status_only_increasesenforces the monotonicity invariant across arbitrary fund amounts.
- String/enum status stored as Symbol: harder to compare in assertions and costs more storage bytes.
- Separate boolean flags (
is_funded,is_settled): allows invalid combinations (both true); integer status is unambiguous.