Source Code

;; DisputeResolution Contract
;; This contract handles the dispute resolution process for the Strade marketplace.
;; It allows users to raise disputes, arbitrators to vote on them, and resolves disputes based on the outcome.

;; --- Constants ---
;; Defines immutable values used throughout the contract for error handling and configuration.

(define-constant CONTRACT_OWNER tx-sender) ;; Sets the contract deployer as the owner.
(define-constant ERR_NOT_AUTHORIZED (err u100)) ;; Error for unauthorized actions.
(define-constant ERR_DISPUTE_NOT_FOUND (err u101)) ;; Error when a dispute cannot be found.
(define-constant ERR_INVALID_STATE (err u102)) ;; Error for invalid dispute states.
(define-constant ERR_NOT_ARBITRATOR (err u103)) ;; Error when a user is not an authorized arbitrator.
(define-constant ERR_ALREADY_VOTED (err u104)) ;; Error if an arbitrator has already voted.
(define-constant ERR_VOTING_CLOSED (err u105)) ;; Error when voting on a dispute is closed.
(define-constant ERR_INSUFFICIENT_VOTES (err u106)) ;; Error if there are not enough votes to resolve a dispute.
(define-constant ERR_INVALID_VOTE (err u107)) ;; Error for invalid vote values.
(define-constant ERR_NOT_INVOLVED_PARTY (err u108)) ;; Error when a user is not a party to the dispute.
(define-constant ERR_INVALID_ESCROW_ID (err u109)) ;; Error for invalid escrow IDs.
(define-constant ERR_INVALID_REASON (err u110)) ;; Error for invalid dispute reasons.
(define-constant ERR_INVALID_DISPUTE_ID (err u111)) ;; Error for invalid dispute IDs.
(define-constant ERR_INVALID_REWARD (err u112)) ;; Error for invalid arbitrator rewards.
(define-constant ERR_INVALID_PRINCIPAL (err u113)) ;; Error for invalid principal addresses.
(define-constant VOTING_PERIOD u144) ;; The duration of the voting period in blocks (approximately 24 hours).
(define-constant MIN_VOTES_REQUIRED u3) ;; The minimum number of votes required to resolve a dispute.

;; --- Data Maps ---
;; Defines the data structures used to store dispute and arbitrator information.

(define-map Disputes
  { dispute-id: uint }
  {
    escrow-id: uint, ;; The ID of the associated escrow.
    initiator: principal, ;; The principal who initiated the dispute.
    counterparty: principal, ;; The other party in the dispute.
    reason: (string-utf8 256), ;; The reason for the dispute.
    status: (string-ascii 20), ;; The current status of the dispute (e.g., "open", "resolved").
    created-at: uint, ;; The block height at which the dispute was created.
    votes-for: uint, ;; The number of votes in favor of the initiator.
    votes-against: uint, ;; The number of votes against the initiator.
    resolution: (optional (string-ascii 20)) ;; The resolution of the dispute.
  }
)

(define-map Arbitrators principal bool) ;; Stores the set of authorized arbitrators.
(define-map ArbitratorVotes { dispute-id: uint, arbitrator: principal } bool) ;; Tracks votes cast by arbitrators.

;; --- Variables ---
;; Defines mutable variables for tracking the contract's state.

(define-data-var last-dispute-id uint u0) ;; Tracks the ID of the last created dispute.
(define-data-var arbitrator-reward uint u100) ;; The reward amount for arbitrators who vote on a dispute.

;; --- Private Functions ---
;; Helper functions intended for internal use by the contract.

;; Checks if a given user is an authorized arbitrator.
(define-private (is-arbitrator (user principal))
  (default-to false (map-get? Arbitrators user))
)

;; Checks if an arbitrator has already voted on a specific dispute.
(define-private (has-voted (dispute-id uint) (arbitrator principal))
  (is-some (map-get? ArbitratorVotes { dispute-id: dispute-id, arbitrator: arbitrator }))
)

;; Updates the vote count for a dispute.
(define-private (update-vote-count (dispute-id uint) (vote bool))
  (match (map-get? Disputes { dispute-id: dispute-id })
    dispute (let
      (
        (new-votes-for (if vote (+ (get votes-for dispute) u1) (get votes-for dispute)))
        (new-votes-against (if vote (get votes-against dispute) (+ (get votes-against dispute) u1)))
      )
      (map-set Disputes { dispute-id: dispute-id }
        (merge dispute {
          votes-for: new-votes-for,
          votes-against: new-votes-against
        }))
      (ok true))
    (err ERR_DISPUTE_NOT_FOUND)
  )
)

;; Checks if an escrow ID is valid.
(define-private (is-valid-escrow-id (escrow-id uint))
  (> escrow-id u0)
)

;; Checks if a dispute reason is valid.
(define-private (is-valid-reason (reason (string-utf8 256)))
  (and (> (len reason) u0) (<= (len reason) u256))
)

;; Checks if a dispute ID is valid.
(define-private (is-valid-dispute-id (dispute-id uint))
  (<= dispute-id (var-get last-dispute-id))
)

;; --- Public Functions ---
;; Functions that can be called by any user.

;; Raises a new dispute.
;; @param escrow-id: The ID of the escrow to dispute.
;; @param counterparty: The other party in the dispute.
;; @param reason: The reason for the dispute.
;; @returns (ok uint): The ID of the newly created dispute.
(define-public (raise-dispute (escrow-id uint) (counterparty principal) (reason (string-utf8 256)))
  (let
    (
      (dispute-id (+ (var-get last-dispute-id) u1))
    )
    (asserts! (is-valid-escrow-id escrow-id) (err ERR_INVALID_ESCROW_ID))
    (asserts! (is-valid-reason reason) (err ERR_INVALID_REASON))
    (asserts! (not (is-eq tx-sender counterparty)) (err ERR_NOT_INVOLVED_PARTY))
    (map-set Disputes
      { dispute-id: dispute-id }
      {
        escrow-id: escrow-id,
        initiator: tx-sender,
        counterparty: counterparty,
        reason: reason,
        status: "open",
        created-at: stacks-block-height,
        votes-for: u0,
        votes-against: u0,
        resolution: none
      }
    )
    (var-set last-dispute-id dispute-id)
    (print { event: "dispute_raised", dispute-id: dispute-id, escrow-id: escrow-id, initiator: tx-sender })
    (ok dispute-id)
  )
)

;; Allows an arbitrator to vote on a dispute.
;; @param dispute-id: The ID of the dispute to vote on.
;; @param vote: The arbitrator's vote (true for initiator, false for counterparty).
;; @returns (ok bool): True if the vote is successful.
(define-public (vote-on-dispute (dispute-id uint) (vote bool))
  (let
    (
      (dispute (unwrap! (map-get? Disputes { dispute-id: dispute-id }) (err ERR_DISPUTE_NOT_FOUND)))
    )
    (asserts! (is-valid-dispute-id dispute-id) (err ERR_INVALID_DISPUTE_ID))
    (asserts! (is-arbitrator tx-sender) (err ERR_NOT_ARBITRATOR))
    (asserts! (is-eq (get status dispute) "open") (err ERR_VOTING_CLOSED))
    (asserts! (<= (- stacks-block-height (get created-at dispute)) VOTING_PERIOD) (err ERR_VOTING_CLOSED))
    (asserts! (not (has-voted dispute-id tx-sender)) (err ERR_ALREADY_VOTED))
    (try! (update-vote-count dispute-id vote))
    (map-set ArbitratorVotes { dispute-id: dispute-id, arbitrator: tx-sender } vote)
    (print { event: "arbitrator_voted", dispute-id: dispute-id, arbitrator: tx-sender, vote: vote })
    (ok true)
  )
)

;; Resolves a dispute based on the votes.
;; @param dispute-id: The ID of the dispute to resolve.
;; @returns (ok (string-ascii 20)): The resolution of the dispute.
(define-public (resolve-dispute (dispute-id uint))
  (let
    (
      (dispute (unwrap! (map-get? Disputes { dispute-id: dispute-id }) (err ERR_DISPUTE_NOT_FOUND)))
    )
    (asserts! (is-valid-dispute-id dispute-id) (err ERR_INVALID_DISPUTE_ID))
    (asserts! (is-eq (get status dispute) "open") (err ERR_INVALID_STATE))
    (asserts! (>= (+ (get votes-for dispute) (get votes-against dispute)) MIN_VOTES_REQUIRED) (err ERR_INSUFFICIENT_VOTES))
    (asserts! (<= (- stacks-block-height (get created-at dispute)) VOTING_PERIOD) (err ERR_VOTING_CLOSED))
    (let
      (
        (resolution (if (> (get votes-for dispute) (get votes-against dispute)) "for_initiator" "for_counterparty"))
      )
      (map-set Disputes { dispute-id: dispute-id }
        (merge dispute {
          status: "resolved",
          resolution: (some resolution)
        })
      )
      (print { event: "dispute_resolved", dispute-id: dispute-id, resolution: resolution })
      (ok resolution)
    )
  )
)

;; Adds a new arbitrator.
;; @param arbitrator: The principal of the new arbitrator.
;; @returns (ok bool): True if the arbitrator is added successfully.
(define-public (add-arbitrator (arbitrator principal))
  (begin
    (asserts! (is-eq tx-sender CONTRACT_OWNER) (err ERR_NOT_AUTHORIZED))
    (asserts! (is-valid-principal arbitrator) (err ERR_INVALID_PRINCIPAL))
    (map-set Arbitrators arbitrator true)
    (print { event: "arbitrator_added", arbitrator: arbitrator })
    (ok true)
  )
)

;; Removes an arbitrator.
;; @param arbitrator: The principal of the arbitrator to remove.
;; @returns (ok bool): True if the arbitrator is removed successfully.
(define-public (remove-arbitrator (arbitrator principal))
  (begin
    (asserts! (is-eq tx-sender CONTRACT_OWNER) (err ERR_NOT_AUTHORIZED))
    (asserts! (is-valid-principal arbitrator) (err ERR_INVALID_PRINCIPAL))
    (map-delete Arbitrators arbitrator)
    (print { event: "arbitrator_removed", arbitrator: arbitrator })
    (ok true)
  )
)

;; Sets the reward for arbitrators.
;; @param new-reward: The new reward amount.
;; @returns (ok bool): True if the reward is set successfully.
(define-public (set-arbitrator-reward (new-reward uint))
  (begin
    (asserts! (is-eq tx-sender CONTRACT_OWNER) (err ERR_NOT_AUTHORIZED))
    (asserts! (> new-reward u0) (err ERR_INVALID_REWARD))
    (var-set arbitrator-reward new-reward)
    (print { event: "arbitrator_reward_set", new-reward: new-reward })
    (ok true)
  )
)

;; --- Read-Only Functions ---
;; Functions for retrieving data from the contract without making state changes.

;; Retrieves a dispute by its ID.
;; @param dispute-id: The ID of the dispute to retrieve.
;; @returns (optional {<dispute-data>}): The dispute data or none if not found.
(define-read-only (get-dispute (dispute-id uint))
  (map-get? Disputes { dispute-id: dispute-id })
)

;; Retrieves the ID of the last created dispute.
;; @returns (ok uint): The last dispute ID.
(define-read-only (get-last-dispute-id)
  (ok (var-get last-dispute-id))
)

;; Retrieves the arbitrator reward amount.
;; @returns (ok uint): The arbitrator reward amount.
(define-read-only (get-arbitrator-reward)
  (ok (var-get arbitrator-reward))
)

;; Checks if a user is an authorized arbitrator.
;; @param user: The principal to check.
;; @returns (ok bool): True if the user is an arbitrator.
(define-read-only (is-user-arbitrator (user principal))
  (ok (is-arbitrator user))
)

;; --- Helper function for principal validation ---
(define-private (is-valid-principal (principal principal))
  (and 
    (not (is-eq principal CONTRACT_OWNER))
    (not (is-eq principal (as-contract tx-sender)))
  )
)

;; --- Contract Initialization ---
;; Initializes the contract upon deployment.
(begin
  (print "DisputeResolution contract initialized")
  (ok true)
)

Functions (17)

FunctionAccessArgs
is-arbitratorprivateuser: principal
has-votedprivatedispute-id: uint, arbitrator: principal
update-vote-countprivatedispute-id: uint, vote: bool
is-valid-escrow-idprivateescrow-id: uint
is-valid-reasonprivatereason: (string-utf8 256
is-valid-dispute-idprivatedispute-id: uint
raise-disputepublicescrow-id: uint, counterparty: principal, reason: (string-utf8 256
vote-on-disputepublicdispute-id: uint, vote: bool
resolve-disputepublicdispute-id: uint
add-arbitratorpublicarbitrator: principal
remove-arbitratorpublicarbitrator: principal
set-arbitrator-rewardpublicnew-reward: uint
get-disputeread-onlydispute-id: uint
get-last-dispute-idread-only
get-arbitrator-rewardread-only
is-user-arbitratorread-onlyuser: principal
is-valid-principalprivateprincipal: principal