feat(escrow): implement formal state machine for milestone workflows - #202
Open
CodeNinjaStephen wants to merge 1 commit into
Open
feat(escrow): implement formal state machine for milestone workflows#202CodeNinjaStephen wants to merge 1 commit into
CodeNinjaStephen wants to merge 1 commit into
Conversation
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)
|
@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! 🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
Multi-milestone contracts revert to
InProgressafter each milestone is released, allowing the workflow to continue for remaining milestones.Changes
New types
ContractStateenum — 9 states stored inDataKey::ContractStateMilestoneStatus::InProgressandMilestoneStatus::ResolvedvariantsError::InvalidContractState(code 13)#[contractevent]macro (replaces deprecatedEvents::publish)New / updated transition functions
initialize()fund()start_work()(new)submit_milestone()approve()freelancer_confirm()release()dispute()resolve_dispute()refund()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.