Source Code

;; Skills-Bridge Escrow Contract
;; Handles job posting, fund locking, and payment release

;; Constants
(define-constant contract-owner tx-sender)
(define-constant err-not-authorized (err u100))
(define-constant err-job-not-found (err u101))
(define-constant err-invalid-status (err u102))
(define-constant err-insufficient-funds (err u103))
(define-constant err-already-accepted (err u104))

;; Data Variables
(define-data-var job-nonce uint u0)
(define-data-var platform-fee-percent uint u3) ;; 3% platform fee

;; Job Status
(define-constant STATUS-OPEN u1)
(define-constant STATUS-ACCEPTED u2)
(define-constant STATUS-SUBMITTED u3)
(define-constant STATUS-COMPLETED u4)
(define-constant STATUS-DISPUTED u5)
(define-constant STATUS-CANCELLED u6)

;; Job Data Structure
(define-map jobs
  uint
  {
    client: principal,
    professional: (optional principal),
    title: (string-ascii 100),
    amount: uint,
    status: uint,
    created-at: uint,
    deadline: uint
  }
)

;; Escrow Balance
(define-map escrow-balance uint uint)

;; Read-only functions
(define-read-only (get-job (job-id uint))
  (map-get? jobs job-id)
)

(define-read-only (get-escrow-balance (job-id uint))
  (default-to u0 (map-get? escrow-balance job-id))
)

(define-read-only (get-platform-fee (amount uint))
  (/ (* amount (var-get platform-fee-percent)) u100)
)

;; Public functions

;; Post a new job and lock funds in escrow
(define-public (post-job (title (string-ascii 100)) (amount uint) (deadline uint))
  (let
    (
      (job-id (+ (var-get job-nonce) u1))
      (fee (get-platform-fee amount))
      (total (+ amount fee))
    )
    (asserts! (> amount u0) err-insufficient-funds)
    (try! (stx-transfer? total tx-sender (as-contract tx-sender)))
    
    (map-set jobs job-id {
      client: tx-sender,
      professional: none,
      title: title,
      amount: amount,
      status: STATUS-OPEN,
      created-at: block-height,
      deadline: deadline
    })
    
    (map-set escrow-balance job-id amount)
    (var-set job-nonce job-id)
    (ok job-id)
  )
)

;; Professional accepts a job
(define-public (accept-job (job-id uint))
  (let
    (
      (job (unwrap! (map-get? jobs job-id) err-job-not-found))
    )
    (asserts! (is-eq (get status job) STATUS-OPEN) err-invalid-status)
    (asserts! (is-none (get professional job)) err-already-accepted)
    
    (map-set jobs job-id (merge job {
      professional: (some tx-sender),
      status: STATUS-ACCEPTED
    }))
    (ok true)
  )
)

;; Professional submits completed work
(define-public (submit-work (job-id uint))
  (let
    (
      (job (unwrap! (map-get? jobs job-id) err-job-not-found))
    )
    (asserts! (is-eq (some tx-sender) (get professional job)) err-not-authorized)
    (asserts! (is-eq (get status job) STATUS-ACCEPTED) err-invalid-status)
    
    (map-set jobs job-id (merge job { status: STATUS-SUBMITTED }))
    (ok true)
  )
)

;; Client approves work and releases payment
(define-public (approve-work (job-id uint))
  (let
    (
      (job (unwrap! (map-get? jobs job-id) err-job-not-found))
      (amount (get-escrow-balance job-id))
      (professional (unwrap! (get professional job) err-not-authorized))
    )
    (asserts! (is-eq tx-sender (get client job)) err-not-authorized)
    (asserts! (is-eq (get status job) STATUS-SUBMITTED) err-invalid-status)
    
    ;; Release funds from escrow to professional
    (try! (as-contract (stx-transfer? amount tx-sender professional)))
    
    (map-set jobs job-id (merge job { status: STATUS-COMPLETED }))
    (map-set escrow-balance job-id u0)
    (ok true)
  )
)

;; Client cancels job (only if not accepted)
(define-public (cancel-job (job-id uint))
  (let
    (
      (job (unwrap! (map-get? jobs job-id) err-job-not-found))
      (amount (get-escrow-balance job-id))
      (fee (get-platform-fee amount))
    )
    (asserts! (is-eq tx-sender (get client job)) err-not-authorized)
    (asserts! (is-eq (get status job) STATUS-OPEN) err-invalid-status)
    
    ;; Refund client (minus platform fee for cancellation)
    (try! (as-contract (stx-transfer? amount tx-sender (get client job))))
    
    (map-set jobs job-id (merge job { status: STATUS-CANCELLED }))
    (map-set escrow-balance job-id u0)
    (ok true)
  )
)

;; Initiate dispute
(define-public (raise-dispute (job-id uint))
  (let
    (
      (job (unwrap! (map-get? jobs job-id) err-job-not-found))
    )
    (asserts! 
      (or 
        (is-eq tx-sender (get client job))
        (is-eq (some tx-sender) (get professional job))
      )
      err-not-authorized
    )
    (asserts! (is-eq (get status job) STATUS-SUBMITTED) err-invalid-status)
    
    (map-set jobs job-id (merge job { status: STATUS-DISPUTED }))
    (ok true)
  )
)

Functions (9)

FunctionAccessArgs
get-jobread-onlyjob-id: uint
get-escrow-balanceread-onlyjob-id: uint
get-platform-feeread-onlyamount: uint
post-jobpublictitle: (string-ascii 100
accept-jobpublicjob-id: uint
submit-workpublicjob-id: uint
approve-workpublicjob-id: uint
cancel-jobpublicjob-id: uint
raise-disputepublicjob-id: uint