Wilfred - #30
Merged
Merged
Conversation
added 2 commits
July 22, 2026 10:00
…truct Add Milestone, MilestoneEscrow, MilestoneEscrowInit, ArbitrationMode, and MilestoneStatus types to the escrow contract. Introduce 12 new error variants (19-30) for milestone-specific validation. Bundle create_milestone_escrow parameters into MilestoneEscrowInit to satisfy clippy's argument-count lint. Include storage layout and authorization model documentation.
Add 26 new tests covering happy paths, failure cases, and adversarial edge cases for the milestone escrow feature. Tests exercise: creation, milestone lifecycle, time-locked release, admin arbitration, partial release accounting, dispute handling, and double-spend prevention. Add PartialEq derive to Milestone for test assertions.
meshackyaro
approved these changes
Jul 23, 2026
meshackyaro
left a comment
Contributor
There was a problem hiding this comment.
Good job @Wilfred007. Thanks for contributing to Guildworkman
11 tasks
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 #14
Summary
Extend the escrow contract to support milestone-based, time-locked partial fund releases with pluggable multi-party arbitration hooks and reentrancy-safe settlement. This enables clients and workers to define granular milestones with individual deadlines and amounts, releasing funds incrementally as work progresses rather than in a single lump sum.
Changes
MilestoneStatus,Milestone,ArbitrationMode,MilestoneEscrow, andMilestoneEscrowInittypes tosoroban-contracts/contracts/escrow/src/lib.rsMilestoneNotFound,InvalidMilestoneAmount,MilestoneAlreadyApproved,MilestoneTimeLocked,MilestoneAlreadyReleased,InvalidEscrowStatus,ArbitrationFailed,NotAClient,NotAWorker,MilestoneAmountMismatch,InvalidMilestoneCount,InvalidDeadlinecreate_milestone_escrow— funds escrow with client deposit, supportsAdminArbiterorExternalHookarbitration modesadd_milestone— client adds milestones with description hash, amount, and time-lock deadline; validates sum equals totalapprove_milestone— client approves a milestone, marking it ready for time-locked releaserelease_milestone_funds— permissionless release after approval + deadline expiry; follows checks-effects-interactions for reentrancy safetyraise_milestone_dispute— either party disputes a pending/approved milestone, freezing the escrowresolve_milestone_dispute— arbiter or external hook resolves dispute, routing funds to client or workerget_milestone_escrowandget_milestoneview functionsDataKey::MilestoneEscrow(u64)for persistent milestone escrow storage (coexists with existingDataKey::Appointment)create_milestone_escrowparameters intoMilestoneEscrowInitstruct to satisfy clippy's 7-argument limitTesting
Command:
cargo test --workspace— 160 tests passing across 6 cratesEscrow crate: 37 tests (11 existing + 26 new)
New milestone escrow tests:
milestone_escrow_create_funds_contract,milestone_escrow_add_approve_release_happy_path,milestone_escrow_admin_arbitration_to_worker,milestone_escrow_admin_arbitration_to_client,partial_release_tracks_accounting_correctly,resolve_dispute_funds_returned_to_contract_balance,get_milestone_view_returns_correct_datamilestone_escrow_zero_amount_rejected,milestone_escrow_negative_amount_rejected,milestone_escrow_duplicate_id_rejected,add_milestone_non_client_rejected,add_milestone_exceeds_total_rejected,add_milestone_past_deadline_rejected,add_milestone_zero_amount_rejected,approve_milestone_already_approved_rejected,approve_milestone_not_found_rejected,release_before_deadline_rejected,release_not_approved_rejected,release_not_found_rejecteddispute_after_release_fails,dispute_by_non_participant_rejected,resolve_dispute_on_non_disputed_milestone_rejected,resolve_dispute_on_already_released_milestone_rejected,release_twice_same_milestone_rejected,dispute_freezes_further_releases,get_milestone_not_found_returns_errorCI checks:
cargo fmt --check— cleancargo clippy --workspace --all-targets -- -D warnings— cleancargo build --workspace --release --target wasm32v1-none— optimized WASM builds successfullyTradeoffs
ExternalHookauthorization: The external hook contract is expected to implement its own authorization logic. The escrow contract verifies the hook address exists but doesn't enforce a specific interface — this keeps the hook pluggable but shifts security responsibility to the hook implementer.sum(milestone.amounts) <= total_amountrather than requiring strict equality, allowing clients to leave a reserve. The remaining funds stay locked until the escrow is completed or resolved.DataKey::MilestoneEscrow(id)which is namespace-separate from the existingDataKey::Appointment(id), so both systems coexist without affecting each other.Architecture
Storage layout:
DataKey::AdminAddressDataKey::Appointment(id)AppointmentDataKey::MilestoneEscrow(id)MilestoneEscrowGovernanceDataKey::*Authorization model:
Reentrancy prevention: All state-mutating functions follow checks-effects-interactions — storage is updated before any
token::Client::transfercall.Out of Scope