Source Code

;; Decentralized Job Board - Clarity 4
;; Post jobs and apply with escrow

(define-constant contract-owner tx-sender)
(define-constant err-not-found (err u3000))
(define-constant err-unauthorized (err u3001))
(define-constant err-already-filled (err u3002))

(define-data-var job-nonce uint u0)
(define-data-var platform-fee uint u250) ;; 2.5%

(define-map jobs
    uint
    {
        employer: principal,
        title: (string-utf8 100),
        description: (string-utf8 500),
        budget: uint,
        deadline: uint,
        filled: bool,
        worker: (optional principal),
        completed: bool
    }
)

(define-map applications
    {job-id: uint, applicant: principal}
    {proposal: (string-utf8 300), applied-at: uint}
)

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

(define-public (post-job
    (title (string-utf8 100))
    (description (string-utf8 500))
    (budget uint)
    (deadline uint)
)
    (let (
        (job-id (var-get job-nonce))
    )
        (try! (stx-transfer? budget tx-sender (as-contract tx-sender)))
        (map-set jobs job-id {
            employer: tx-sender,
            title: title,
            description: description,
            budget: budget,
            deadline: deadline,
            filled: false,
            worker: none,
            completed:false
        })
        (var-set job-nonce (+ job-id u1))
        (ok job-id)
    )
)

(define-public (apply-for-job (job-id uint) (proposal (string-utf8 300)))
    (let (
        (job (unwrap! (get-job job-id) err-not-found))
    )
        (asserts! (not (get filled job)) err-already-filled)
        (ok (map-set applications {job-id: job-id, applicant: tx-sender}
            {proposal: proposal, applied-at: stacks-block-height}
        ))
    )
)

(define-public (hire-worker (job-id uint) (worker principal))
    (let (
        (job (unwrap! (get-job job-id) err-not-found))
    )
        (asserts! (is-eq tx-sender (get employer job)) err-unauthorized)
        (asserts! (not (get filled job)) err-already-filled)
        
        (ok (map-set jobs job-id 
            (merge job {filled: true, worker: (some worker)})
        ))
    )
)

(define-public (complete-job (job-id uint))
    (let (
        (job (unwrap! (get-job job-id) err-not-found))
        (worker (unwrap! (get worker job) err-not-found))
        (fee (/ (* (get budget job) (var-get platform-fee)) u10000))
        (payment (- (get budget job) fee))
    )
        (asserts! (is-eq tx-sender (get employer job)) err-unauthorized)
        (asserts! (get filled job) err-not-found)
        
        (map-set jobs job-id (merge job {completed: true}))
        
        (try! (as-contract (stx-transfer? payment tx-sender worker)))
        (try! (as-contract (stx-transfer? fee tx-sender contract-owner)))
        (ok true)
    )
)

(define-public (cancel-job (job-id uint))
    (let (
        (job (unwrap! (get-job job-id) err-not-found))
    )
        (asserts! (is-eq tx-sender (get employer job)) err-unauthorized)
        (asserts! (not (get filled job)) err-already-filled)
        
        (try! (as-contract (stx-transfer? (get budget job) tx-sender (get employer job))))
        (ok true)
    )
)

Functions (6)

FunctionAccessArgs
get-jobread-onlyjob-id: uint
post-jobpublictitle: (string-utf8 100
apply-for-jobpublicjob-id: uint, proposal: (string-utf8 300
hire-workerpublicjob-id: uint, worker: principal
complete-jobpublicjob-id: uint
cancel-jobpublicjob-id: uint