Source Code

;; Dispute Arbitration Contract (Clarity 4)
;; Decentralized dispute resolution for failed exchanges
;; Uses stacks-block-time for voting deadlines and evidence submission

;; constants
(define-constant CONTRACT_OWNER tx-sender)
(define-constant ERR_UNAUTHORIZED (err u4001))
(define-constant ERR_NOT_FOUND (err u4002))
(define-constant ERR_INVALID_STATUS (err u4003))
(define-constant ERR_ALREADY_VOTED (err u4004))
(define-constant ERR_DEADLINE_PASSED (err u4005))
(define-constant ERR_DEADLINE_NOT_PASSED (err u4006))

(define-constant STATUS_PENDING u1)
(define-constant STATUS_VOTING u2)
(define-constant STATUS_RESOLVED u3)
(define-constant STATUS_EXECUTED u4)

(define-constant OUTCOME_FAVOR_PROVIDER u1)
(define-constant OUTCOME_FAVOR_RECEIVER u2)
(define-constant OUTCOME_SPLIT u3)

(define-constant EVIDENCE_PERIOD u604800)
(define-constant VOTING_PERIOD u259200)
(define-constant MIN_ARBITRATORS u3)

;; data vars
(define-data-var dispute-counter uint u0)
(define-data-var arbitrator-counter uint u0)

;; data maps
(define-map arbitrators
    principal
    {
        reputation-score: uint,
        total-cases: uint,
        is-active: bool,
        joined-at: uint
    })

(define-map disputes
    uint
    {
        exchange-id: uint,
        provider: principal,
        receiver: principal,
        status: uint,
        outcome: (optional uint),
        created-at: uint,
        evidence-deadline: uint,
        voting-deadline: uint,
        votes-provider: uint,
        votes-receiver: uint,
        votes-split: uint
    })

(define-map votes
    { dispute-id: uint, arbitrator: principal }
    {
        vote: uint,
        voted-at: uint
    })

(define-map dispute-arbitrators
    { dispute-id: uint, arbitrator: principal }
    bool)

;; public functions
(define-public (register-arbitrator)
    (begin
        (asserts! (is-none (map-get? arbitrators tx-sender)) ERR_INVALID_STATUS)
        (map-set arbitrators tx-sender {
            reputation-score: u0,
            total-cases: u0,
            is-active: true,
            joined-at: stacks-block-time
        })
        (var-set arbitrator-counter (+ (var-get arbitrator-counter) u1))
        (ok true)))

(define-public (create-dispute
    (exchange-id uint)
    (provider principal)
    (receiver principal))
    (let ((dispute-id (+ (var-get dispute-counter) u1)))
        (asserts! (or (is-eq tx-sender provider) (is-eq tx-sender receiver)) ERR_UNAUTHORIZED)
        (map-set disputes dispute-id {
            exchange-id: exchange-id,
            provider: provider,
            receiver: receiver,
            status: STATUS_PENDING,
            outcome: none,
            created-at: stacks-block-time,
            evidence-deadline: (+ stacks-block-time EVIDENCE_PERIOD),
            voting-deadline: (+ stacks-block-time (+ EVIDENCE_PERIOD VOTING_PERIOD)),
            votes-provider: u0,
            votes-receiver: u0,
            votes-split: u0
        })
        (var-set dispute-counter dispute-id)
        (ok dispute-id)))

(define-public (assign-to-dispute (dispute-id uint))
    (let ((arb-data (unwrap! (map-get? arbitrators tx-sender) ERR_UNAUTHORIZED)))
        (asserts! (get is-active arb-data) ERR_UNAUTHORIZED)
        (asserts! (is-none (map-get? dispute-arbitrators { dispute-id: dispute-id, arbitrator: tx-sender })) ERR_ALREADY_VOTED)
        (map-set dispute-arbitrators { dispute-id: dispute-id, arbitrator: tx-sender } true)
        (ok true)))

(define-public (cast-vote (dispute-id uint) (vote uint))
    (let ((dispute (unwrap! (map-get? disputes dispute-id) ERR_NOT_FOUND)))
        (asserts! (default-to false (map-get? dispute-arbitrators { dispute-id: dispute-id, arbitrator: tx-sender })) ERR_UNAUTHORIZED)
        (asserts! (is-none (map-get? votes { dispute-id: dispute-id, arbitrator: tx-sender })) ERR_ALREADY_VOTED)
        (asserts! (and (>= stacks-block-time (get evidence-deadline dispute)) (< stacks-block-time (get voting-deadline dispute))) ERR_DEADLINE_PASSED)
        (map-set votes { dispute-id: dispute-id, arbitrator: tx-sender } {
            vote: vote,
            voted-at: stacks-block-time
        })
        (map-set disputes dispute-id (merge dispute {
            status: STATUS_VOTING,
            votes-provider: (if (is-eq vote OUTCOME_FAVOR_PROVIDER) (+ (get votes-provider dispute) u1) (get votes-provider dispute)),
            votes-receiver: (if (is-eq vote OUTCOME_FAVOR_RECEIVER) (+ (get votes-receiver dispute) u1) (get votes-receiver dispute)),
            votes-split: (if (is-eq vote OUTCOME_SPLIT) (+ (get votes-split dispute) u1) (get votes-split dispute))
        }))
        (ok true)))

(define-public (resolve-dispute (dispute-id uint))
    (let ((dispute (unwrap! (map-get? disputes dispute-id) ERR_NOT_FOUND)))
        (asserts! (>= stacks-block-time (get voting-deadline dispute)) ERR_DEADLINE_NOT_PASSED)
        (asserts! (is-eq (get status dispute) STATUS_VOTING) ERR_INVALID_STATUS)
        (let ((outcome (determine-outcome
                (get votes-provider dispute)
                (get votes-receiver dispute)
                (get votes-split dispute))))
            (map-set disputes dispute-id (merge dispute {
                status: STATUS_RESOLVED,
                outcome: (some outcome)
            }))
            (ok outcome))))

;; read only functions
(define-read-only (get-dispute (dispute-id uint))
    (ok (map-get? disputes dispute-id)))

(define-read-only (get-arbitrator-info (arbitrator principal))
    (ok (map-get? arbitrators arbitrator)))

(define-read-only (get-vote (dispute-id uint) (arbitrator principal))
    (ok (map-get? votes { dispute-id: dispute-id, arbitrator: arbitrator })))

;; private functions
(define-private (determine-outcome (votes-provider uint) (votes-receiver uint) (votes-split uint))
    (if (and (> votes-provider votes-receiver) (> votes-provider votes-split))
        OUTCOME_FAVOR_PROVIDER
        (if (and (> votes-receiver votes-provider) (> votes-receiver votes-split))
            OUTCOME_FAVOR_RECEIVER
            OUTCOME_SPLIT)))

Functions (9)

FunctionAccessArgs
register-arbitratorpublic
create-disputepublicexchange-id: uint, provider: principal, receiver: principal
assign-to-disputepublicdispute-id: uint
cast-votepublicdispute-id: uint, vote: uint
resolve-disputepublicdispute-id: uint
get-disputeread-onlydispute-id: uint
get-arbitrator-inforead-onlyarbitrator: principal
get-voteread-onlydispute-id: uint, arbitrator: principal
determine-outcomeprivatevotes-provider: uint, votes-receiver: uint, votes-split: uint