;; prime-hold - Trustless Escrow Protocol
;; Secure multi-party payment escrow
(define-constant CONTRACT-OWNER tx-sender)
(define-constant PLATFORM-FEE u100)
(define-constant FEE-DENOMINATOR u10000)
(define-constant DISPUTE-PERIOD u1008)
(define-constant ERR-NOT-FOUND (err u100))
(define-constant ERR-NOT-AUTHORIZED (err u101))
(define-constant ERR-ALREADY-COMPLETE (err u102))
(define-constant ERR-DISPUTE-PERIOD (err u103))
(define-constant ERR-WRONG-AMOUNT (err u104))
(define-constant ERR-ZERO-AMOUNT (err u105))
(define-map escrows uint
{ buyer: principal, seller: principal, amount: uint,
description: (string-utf8 256), created-at: uint,
released: bool, disputed: bool, canceled: bool,
arbiter: (optional principal) })
(define-data-var escrow-count uint u0)
(define-data-var total-locked uint u0)
(define-data-var fees-collected uint u0)
(define-public (create-escrow
(seller principal) (amount uint)
(description (string-utf8 256))
(arbiter (optional principal)))
(let ((eid (+ (var-get escrow-count) u1)))
(asserts! (> amount u0) ERR-ZERO-AMOUNT)
(try! (stx-transfer? amount tx-sender (as-contract tx-sender)))
(map-set escrows eid
{ buyer: tx-sender, seller: seller, amount: amount,
description: description, created-at: block-height,
released: false, disputed: false, canceled: false,
arbiter: arbiter })
(var-set escrow-count eid)
(var-set total-locked (+ (var-get total-locked) amount))
(ok eid)))
(define-public (release (escrow-id uint))
(let ((e (unwrap! (map-get? escrows escrow-id) ERR-NOT-FOUND)))
(asserts! (is-eq tx-sender (get buyer e)) ERR-NOT-AUTHORIZED)
(asserts! (not (get released e)) ERR-ALREADY-COMPLETE)
(asserts! (not (get canceled e)) ERR-ALREADY-COMPLETE)
(let ((fee (/ (* (get amount e) PLATFORM-FEE) FEE-DENOMINATOR))
(net (- (get amount e) fee)))
(try! (as-contract (stx-transfer? net tx-sender (get seller e))))
(try! (as-contract (stx-transfer? fee tx-sender CONTRACT-OWNER)))
(map-set escrows escrow-id (merge e { released: true }))
(var-set total-locked (- (var-get total-locked) (get amount e)))
(var-set fees-collected (+ (var-get fees-collected) fee))
(ok true))))
(define-public (raise-dispute (escrow-id uint))
(let ((e (unwrap! (map-get? escrows escrow-id) ERR-NOT-FOUND)))
(asserts! (or (is-eq tx-sender (get buyer e)) (is-eq tx-sender (get seller e))) ERR-NOT-AUTHORIZED)
(asserts! (not (get released e)) ERR-ALREADY-COMPLETE)
(asserts! (<= block-height (+ (get created-at e) DISPUTE-PERIOD)) ERR-DISPUTE-PERIOD)
(ok (map-set escrows escrow-id (merge e { disputed: true })))))
(define-public (resolve-dispute (escrow-id uint) (favor-buyer bool))
(let ((e (unwrap! (map-get? escrows escrow-id) ERR-NOT-FOUND)))
(asserts! (is-eq (some tx-sender) (get arbiter e)) ERR-NOT-AUTHORIZED)
(asserts! (get disputed e) ERR-NOT-FOUND)
(let ((recipient (if favor-buyer (get buyer e) (get seller e))))
(try! (as-contract (stx-transfer? (get amount e) tx-sender recipient)))
(map-set escrows escrow-id (merge e { released: true }))
(ok true))))
(define-read-only (get-escrow (escrow-id uint)) (ok (map-get? escrows escrow-id)))
(define-read-only (get-escrow-count) (ok (var-get escrow-count)))
(define-read-only (get-platform-stats) (ok { locked: (var-get total-locked), fees: (var-get fees-collected) }))