Skip to content

Issue #4 — Dispute Mechanism #588

@yusuftomilola

Description

@yusuftomilola

Admin-controlled release and refund (Issue #3) work well for normal cases, but a depositor may disagree with the admin's decision before it is made. The dispute mechanism gives depositors a time-limited window to flag an escrow for review. Once disputed, funds are frozen until the admin explicitly resolves the case — preventing unilateral admin action during an open dispute.

Task

Building on the code from Issue #3, implement the following in contracts/payment_escrow/src/lib.rs:

  1. raise_dispute — depositor-initiated function that:

    • Requires the caller's signature
    • Validates the caller is the escrow's depositor (returns Unauthorized otherwise)
    • Rejects if status is not Pending (returns EscrowNotPending)
    • Rejects if the escrow's dispute_window == 0 (disputes disabled — returns DisputeWindowClosed)
    • Rejects if the current ledger timestamp exceeds created_at + dispute_window (window expired — returns DisputeWindowClosed)
    • Updates status to Disputed and sets dispute_raised_at to current timestamp
    • Saves the updated escrow
    • Emits a "disputed" event
  2. resolve_dispute — admin-only function that:

    • Requires admin authorization
    • Loads the escrow by ID
    • Rejects if status is not Disputed (returns EscrowNotDisputed)
    • Accepts a release_to_beneficiary: bool parameter:
      • true → transfers funds to beneficiary, sets status to Released
      • false → transfers funds to depositor, sets status to Refunded
    • Sets resolved_at to current timestamp
    • Saves the updated escrow
    • Emits a "resolved" event

Files to Modify

  • contracts/payment_escrow/src/lib.rs

Acceptance Criteria

  • raise_dispute rejects callers who are not the depositor
  • raise_dispute rejects escrows not in Pending status
  • raise_dispute rejects when dispute_window == 0
  • raise_dispute rejects when the dispute window has expired
  • raise_dispute correctly transitions status to Disputed
  • raise_dispute records dispute_raised_at
  • resolve_dispute rejects non-admin callers
  • resolve_dispute rejects escrows not in Disputed status
  • resolve_dispute correctly routes funds based on release_to_beneficiary flag
  • resolve_dispute correctly sets final status (Released or Refunded)
  • cargo clippy -p payment_escrow -- -D warnings passes

Technical Notes

  • The dispute window check: env.ledger().timestamp() > escrow.created_at + escrow.dispute_window
  • For resolve_dispute, compute the recipient address before the token transfer, then use it in both the transfer call and the event
  • EscrowStatus::Disputed is defined in types.rs — the release and refund functions from Issue Hrushi backend nestjs setup #3 do NOT handle Disputed status, which is intentional; resolve_dispute is the only exit path for disputed escrows

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions