Source Code

;; Task Manager Contract
;; Core task lifecycle management

;; Constants
(define-constant CONTRACT-OWNER tx-sender)
(define-constant ERR-NOT-AUTHORIZED (err u300))
(define-constant ERR-TASK-NOT-FOUND (err u301))
(define-constant ERR-INVALID-STATUS (err u302))
(define-constant ERR-TASK-ALREADY-ASSIGNED (err u303))
(define-constant ERR-TASK-NOT-ASSIGNED (err u304))
(define-constant ERR-DEADLINE-PASSED (err u305))
(define-constant ERR-INVALID-TITLE (err u306))
(define-constant ERR-INVALID-DESCRIPTION (err u307))
(define-constant ERR-INVALID-CATEGORY (err u308))
(define-constant ERR-ESCROW-FAILED (err u309))
(define-constant ERR-ALREADY-SUBMITTED (err u310))
(define-constant ERR-NOT-SUBMITTED (err u311))

;; Task Status Constants
(define-constant STATUS-OPEN u0)
(define-constant STATUS-ASSIGNED u1)
(define-constant STATUS-SUBMITTED u2)
(define-constant STATUS-APPROVED u3)
(define-constant STATUS-DISPUTED u4)
(define-constant STATUS-CANCELLED u5)
(define-constant STATUS-REJECTED u6)

;; Data Variables
(define-data-var task-counter uint u0)

;; Data Maps
(define-map tasks
    { task-id: uint }
    {
        creator: principal,
        worker: (optional principal),
        title: (string-ascii 100),
        description: (string-utf8 500),
        category: (string-ascii 50),
        reward: uint,
        status: uint,
        created-at: uint,
        deadline: uint,
        submission-url: (optional (string-utf8 200)),
        submission-notes: (optional (string-utf8 300)),
        submitted-at: (optional uint),
        completed-at: (optional uint)
    }
)

;; Track tasks by creator
(define-map creator-tasks
    { creator: principal, task-id: uint }
    bool
)

;; Track tasks by worker
(define-map worker-tasks
    { worker: principal, task-id: uint }
    bool
)

;; Contract references (to be set after deployment)
(define-data-var escrow-contract (optional principal) none)
(define-data-var reputation-contract (optional principal) none)

;; Read-only functions

(define-read-only (get-task (task-id uint))
    (ok (map-get? tasks { task-id: task-id }))
)

(define-read-only (get-task-count)
    (ok (var-get task-counter))
)

(define-read-only (get-escrow-contract)
    (ok (var-get escrow-contract))
)

(define-read-only (get-reputation-contract)
    (ok (var-get reputation-contract))
)

(define-read-only (is-task-creator (task-id uint) (user principal))
    (match (map-get? tasks { task-id: task-id })
        task-data (ok (is-eq (get creator task-data) user))
        (ok false)
    )
)

(define-read-only (is-task-worker (task-id uint) (user principal))
    (match (map-get? tasks { task-id: task-id })
        task-data 
        (match (get worker task-data)
            worker (ok (is-eq worker user))
            (ok false)
        )
        (ok false)
    )
)

;; Private functions

(define-private (validate-title (title (string-ascii 100)))
    (> (len title) u0)
)

(define-private (validate-description (description (string-utf8 500)))
    (> (len description) u0)
)

(define-private (validate-category (category (string-ascii 50)))
    (> (len category) u0)
)

;; Public functions

(define-public (set-escrow-contract (contract principal))
    (begin
        (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-NOT-AUTHORIZED)
        (var-set escrow-contract (some contract))
        (ok true)
    )
)

(define-public (set-reputation-contract (contract principal))
    (begin
        (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-NOT-AUTHORIZED)
        (var-set reputation-contract (some contract))
        (ok true)
    )
)

(define-public (create-task 
    (title (string-ascii 100))
    (description (string-utf8 500))
    (category (string-ascii 50))
    (reward uint)
    (deadline uint))
    (let (
        (task-id (+ (var-get task-counter) u1))
    )
    
    ;; Validate inputs
    (asserts! (validate-title title) ERR-INVALID-TITLE)
    (asserts! (validate-description description) ERR-INVALID-DESCRIPTION)
    (asserts! (validate-category category) ERR-INVALID-CATEGORY)
    (asserts! (> deadline block-height) ERR-DEADLINE-PASSED)
    
    ;; Ensure escrow contract is set
    (asserts! (is-some (var-get escrow-contract)) ERR-ESCROW-FAILED)
    
    ;; Deposit escrow
    (unwrap! (contract-call? .task-escrow deposit-escrow task-id reward tx-sender) ERR-ESCROW-FAILED)
    
    ;; Record task creation in reputation contract (if set)
    (match (var-get reputation-contract)
        rep-contract (try! (contract-call? .reputation-tracker record-task-created tx-sender reward))
        true
    )
    
    ;; Create task
    (map-set tasks
        { task-id: task-id }
        {
            creator: tx-sender,
            worker: none,
            title: title,
            description: description,
            category: category,
            reward: reward,
            status: STATUS-OPEN,
            created-at: block-height,
            deadline: deadline,
            submission-url: none,
            submission-notes: none,
            submitted-at: none,
            completed-at: none
        }
    )
    
    ;; Track task for creator
    (map-set creator-tasks { creator: tx-sender, task-id: task-id } true)
    
    ;; Increment counter
    (var-set task-counter task-id)
    
    (ok task-id))
)

(define-public (assign-task (task-id uint) (worker principal))
    (let (
        (task-data (unwrap! (map-get? tasks { task-id: task-id }) ERR-TASK-NOT-FOUND))
    )
    
    ;; Only creator can assign
    (asserts! (is-eq tx-sender (get creator task-data)) ERR-NOT-AUTHORIZED)
    
    ;; Must be in OPEN status
    (asserts! (is-eq (get status task-data) STATUS-OPEN) ERR-INVALID-STATUS)
    
    ;; Check deadline hasn't passed
    (asserts! (< block-height (get deadline task-data)) ERR-DEADLINE-PASSED)
    
    ;; Update worker in escrow
    (unwrap! (contract-call? .task-escrow set-worker task-id worker) ERR-ESCROW-FAILED)
    
    ;; Update task
    (map-set tasks
        { task-id: task-id }
        (merge task-data {
            worker: (some worker),
            status: STATUS-ASSIGNED
        })
    )
    
    ;; Track task for worker
    (map-set worker-tasks { worker: worker, task-id: task-id } true)
    
    (ok true))
)

(define-public (submit-work 
    (task-id uint) 
    (submission-url (string-utf8 200))
    (notes (string-utf8 300)))
    (let (
        (task-data (unwrap! (map-get? tasks { task-id: task-id }) ERR-TASK-NOT-FOUND))
        (worker (unwrap! (get worker task-data) ERR-TASK-NOT-ASSIGNED))
    )
    
    ;; Only assigned worker can submit
    (asserts! (is-eq tx-sender worker) ERR-NOT-AUTHORIZED)
    
    ;; Must be in ASSIGNED status
    (asserts! (is-eq (get status task-data) STATUS-ASSIGNED) ERR-INVALID-STATUS)
    
    ;; Check deadline hasn't passed
    (asserts! (< block-height (get deadline task-data)) ERR-DEADLINE-PASSED)
    
    ;; Update task
    (map-set tasks
        { task-id: task-id }
        (merge task-data {
            status: STATUS-SUBMITTED,
            submission-url: (some submission-url),
            submission-notes: (some notes),
            submitted-at: (some block-height)
        })
    )
    
    (ok true))
)

(define-public (approve-task (task-id uint) (rating uint))
    (let (
        (task-data (unwrap! (map-get? tasks { task-id: task-id }) ERR-TASK-NOT-FOUND))
        (worker (unwrap! (get worker task-data) ERR-TASK-NOT-ASSIGNED))
        (reward (get reward task-data))
        (reputation-contract-addr (var-get reputation-contract))
    )
    
    ;; Only creator can approve
    (asserts! (is-eq tx-sender (get creator task-data)) ERR-NOT-AUTHORIZED)
    
    ;; Must be in SUBMITTED status
    (asserts! (is-eq (get status task-data) STATUS-SUBMITTED) ERR-INVALID-STATUS)
    
    ;; Release funds from escrow
    (unwrap! (contract-call? .task-escrow release-funds task-id) ERR-ESCROW-FAILED)
    
    ;; Record completion in reputation contract (if set)
    (match reputation-contract-addr
        rep-contract (begin
            (unwrap! (contract-call? .reputation-tracker record-completion worker true rating) ERR-ESCROW-FAILED)
            (unwrap! (contract-call? .reputation-tracker record-task-earned worker reward) ERR-ESCROW-FAILED)
            true
        )
        true
    )
    
    ;; Update task
    (map-set tasks
        { task-id: task-id }
        (merge task-data {
            status: STATUS-APPROVED,
            completed-at: (some block-height)
        })
    )
    
    (ok true))
)

(define-public (reject-task (task-id uint) (reason (string-utf8 200)))
    (let (
        (task-data (unwrap! (map-get? tasks { task-id: task-id }) ERR-TASK-NOT-FOUND))
    )
    
    ;; Only creator can reject
    (asserts! (is-eq tx-sender (get creator task-data)) ERR-NOT-AUTHORIZED)
    
    ;; Must be in SUBMITTED status
    (asserts! (is-eq (get status task-data) STATUS-SUBMITTED) ERR-INVALID-STATUS)
    
    ;; Refund creator from escrow
    (unwrap! (contract-call? .task-escrow refund-creator task-id) ERR-ESCROW-FAILED)
    
    ;; Update task
    (map-set tasks
        { task-id: task-id }
        (merge task-data {
            status: STATUS-REJECTED
        })
    )
    
    (ok true))
)

(define-public (open-dispute (task-id uint))
    (let (
        (task-data (unwrap! (map-get? tasks { task-id: task-id }) ERR-TASK-NOT-FOUND))
        (worker (unwrap! (get worker task-data) ERR-TASK-NOT-ASSIGNED))
    )
    
    ;; Only creator or worker can open dispute
    (asserts! (or 
        (is-eq tx-sender (get creator task-data))
        (is-eq tx-sender worker)
    ) ERR-NOT-AUTHORIZED)
    
    ;; Must be in SUBMITTED or REJECTED status
    (asserts! (or
        (is-eq (get status task-data) STATUS-SUBMITTED)
        (is-eq (get status task-data) STATUS-REJECTED)
    ) ERR-INVALID-STATUS)
    
    ;; Open dispute in escrow
    (unwrap! (contract-call? .task-escrow open-dispute task-id) ERR-ESCROW-FAILED)
    
    ;; Update task
    (map-set tasks
        { task-id: task-id }
        (merge task-data {
            status: STATUS-DISPUTED
        })
    )
    
    (ok true))
)

(define-public (cancel-task (task-id uint))
    (let (
        (task-data (unwrap! (map-get? tasks { task-id: task-id }) ERR-TASK-NOT-FOUND))
    )
    
    ;; Only creator can cancel
    (asserts! (is-eq tx-sender (get creator task-data)) ERR-NOT-AUTHORIZED)
    
    ;; Can only cancel if OPEN or ASSIGNED
    (asserts! (or
        (is-eq (get status task-data) STATUS-OPEN)
        (is-eq (get status task-data) STATUS-ASSIGNED)
    ) ERR-INVALID-STATUS)
    
    ;; Refund creator from escrow
    (unwrap! (contract-call? .task-escrow refund-creator task-id) ERR-ESCROW-FAILED)
    
    ;; Update task
    (map-set tasks
        { task-id: task-id }
        (merge task-data {
            status: STATUS-CANCELLED
        })
    )
    
    (ok true))
)

Functions (18)

FunctionAccessArgs
get-taskread-onlytask-id: uint
get-task-countread-only
get-escrow-contractread-only
get-reputation-contractread-only
is-task-creatorread-onlytask-id: uint, user: principal
is-task-workerread-onlytask-id: uint, user: principal
validate-titleprivatetitle: (string-ascii 100
validate-descriptionprivatedescription: (string-utf8 500
validate-categoryprivatecategory: (string-ascii 50
set-escrow-contractpubliccontract: principal
set-reputation-contractpubliccontract: principal
create-taskpublictitle: (string-ascii 100
assign-taskpublictask-id: uint, worker: principal
submit-workpublictask-id: uint, submission-url: (string-utf8 200
approve-taskpublictask-id: uint, rating: uint
reject-taskpublictask-id: uint, reason: (string-utf8 200
open-disputepublictask-id: uint
cancel-taskpublictask-id: uint