Skip to content

feat(escrow): implement formal state machine for milestone workflows - #202

Open
CodeNinjaStephen wants to merge 1 commit into
Lumina-eX:mainfrom
CodeNinjaStephen:feat/escrow-state-machine
Open

feat(escrow): implement formal state machine for milestone workflows#202
CodeNinjaStephen wants to merge 1 commit into
Lumina-eX:mainfrom
CodeNinjaStephen:feat/escrow-state-machine

Conversation

@CodeNinjaStephen

@CodeNinjaStephen CodeNinjaStephen commented Aug 26, 2026

Copy link
Copy Markdown

Closes #189


Summary

Implements a formal contract-level state machine inside the Soroban escrow contract, enforcing predictable milestone-based workflows with strict authorization and transparent event emission.

Closes the state machine issue: defines states, enforces transitions, adds role-based auth, emits typed events, and covers all paths with 33 contract tests.

State Machine

Created → Funded → InProgress → Submitted → Approved → Released
                ↘              ↘            ↘          ↘
              Refunded       Disputed     Disputed    Disputed
                                 ↓
                              Resolved

Multi-milestone contracts revert to InProgress after each milestone is released, allowing the workflow to continue for remaining milestones.

Changes

New types

  • ContractState enum — 9 states stored in DataKey::ContractState
  • MilestoneStatus::InProgress and MilestoneStatus::Resolved variants
  • Error::InvalidContractState (code 13)
  • 11 typed contract events via #[contractevent] macro (replaces deprecated Events::publish)

New / updated transition functions

Function Transition Auth
initialize() → Created
fund() Created → Funded client
start_work() (new) Funded → InProgress freelancer
submit_milestone() InProgress → Submitted freelancer
approve() Submitted → Approved client
freelancer_confirm() sub-step in Approved freelancer
release() Approved → Released client or freelancer
dispute() Funded/InProgress/Submitted/Approved → Disputed client or freelancer
resolve_dispute() Disputed → Resolved arbiter
refund() Funded/InProgress/Submitted → Refunded client or freelancer

Events

Every transition emits a StateTransition { from, to } event plus a function-specific typed event, giving off-chain indexers a complete audit trail.

Tests

33 tests, 0 failures, 0 warnings — cargo test -p escrow.

Valid transition coverage: full happy path, multi-milestone, dispute→resolved (both outcomes), refund from each valid state, state getter at every step, dispute clears approvals.

Invalid transition coverage (18 negative tests): double fund/init/start_work, out-of-order calls, unauthorized callers, missing approvals, illegal states for refund/dispute/resolve.

Add a contract-level ContractState enum with strict transition enforcement,
role-based authorization, typed events, and comprehensive tests.

States: Created → Funded → InProgress → Submitted → Approved → Released
Extra:  Submitted/Approved → Disputed → Resolved
        Funded/InProgress/Submitted → Refunded

Changes:
- Add ContractState enum (9 states) stored in DataKey::ContractState
- Add MilestoneStatus::InProgress and MilestoneStatus::Resolved variants
- Add start_work() transition: Funded → InProgress (freelancer auth)
- update submit_milestone() to require InProgress contract state
- update approve() to require Submitted contract state
- update release() to require Approved + both confirmations
- update dispute() to accept Funded/InProgress/Submitted/Approved states
- update resolve_dispute() to transition to new Resolved state
- update refund() to accept Funded/InProgress/Submitted states
- Add Error::InvalidContractState (code 13) for illegal transitions
- Add emit_transition() helper emitting StateTransition{from,to} event
- Replace deprecated Events::publish calls with #[contractevent] typed structs
- Multi-milestone: release() reverts to InProgress when milestones remain
- Add get_contract_state() public getter

Tests (33 total, all passing):
- Happy path: full Created→Released state machine
- Multi-milestone workflow with mid-contract state revert
- Submitted→Disputed→Resolved (freelancer wins)
- Approved→Disputed→Resolved (client wins)
- InProgress disputed by freelancer
- Funded→Refunded by client / InProgress→Refunded / Submitted→Refunded
- State getter verified at every step
- Dispute clears approval flags
- All invalid transitions rejected (18 negative tests)
@drips-wave

drips-wave Bot commented Aug 26, 2026

Copy link
Copy Markdown

@CodeNinjaStephen Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Soroban Milestone Escrow State Machine

1 participant