(define-constant err-not-found (err u404))
(define-constant err-already-exists (err u409))
(define-constant err-forbidden (err u403))
(define-constant err-goal-not-met (err u400))
(define-constant err-deadline-not-passed (err u401))
(define-constant err-already-claimed (err u402))
(define-constant err-deadline-passed (err u405))
(define-constant err-campaign-cancelled (err u406))
(define-constant err-campaign-active (err u407))
(define-constant status-active u0)
(define-constant status-funded u1)
(define-constant status-cancelled u2)
(define-data-var next-campaign-id uint u1)
(define-map campaigns uint {owner:principal,title:(string-ascii 50),description:(string-ascii 256),category:(string-ascii 20),goal:uint,deadline:uint,pledged:uint,status:uint,claimed:bool})
(define-map pledges {campaign-id:uint,backer:principal} {amount:uint})
(define-public (create-campaign (title (string-ascii 50)) (description (string-ascii 256)) (category (string-ascii 20)) (goal uint) (deadline uint))
(let ((campaign-id (var-get next-campaign-id)))
(map-set campaigns campaign-id {owner:tx-sender,title:title,description:description,category:category,goal:goal,deadline:deadline,pledged:u0,status:status-active,claimed:false})
(var-set next-campaign-id (+ campaign-id u1))
(ok campaign-id)))
(define-public (pledge (campaign-id uint) (amount uint))
(let ((campaign (unwrap! (map-get? campaigns campaign-id) err-not-found))
(current-pledged (get pledged campaign))
(backer-pledge (default-to {amount:u0} (map-get? pledges {campaign-id:campaign-id,backer:tx-sender})))
(contract-principal (as-contract tx-sender)))
(asserts! (is-eq (get status campaign) status-active) err-campaign-cancelled)
(asserts! (< block-height (get deadline campaign)) err-deadline-passed)
(try! (stx-transfer? amount tx-sender contract-principal))
(map-set pledges {campaign-id:campaign-id,backer:tx-sender} {amount:(+ amount (get amount backer-pledge))})
(map-set campaigns campaign-id (merge campaign {pledged:(+ current-pledged amount)}))
(ok true)))
(define-public (claim-funds (campaign-id uint))
(let ((campaign (unwrap! (map-get? campaigns campaign-id) err-not-found))
(pledged-amount (get pledged campaign)))
(asserts! (is-eq (get owner campaign) tx-sender) err-forbidden)
(asserts! (>= pledged-amount (get goal campaign)) err-goal-not-met)
(asserts! (not (get claimed campaign)) err-already-claimed)
(asserts! (is-eq (get status campaign) status-active) err-campaign-cancelled)
(try! (as-contract (stx-transfer? pledged-amount tx-sender (get owner campaign))))
(map-set campaigns campaign-id (merge campaign {claimed:true,status:status-funded}))
(ok true)))
(define-public (refund (campaign-id uint))
(let ((campaign (unwrap! (map-get? campaigns campaign-id) err-not-found))
(pledged-amount (get pledged campaign))
(backer-pledge (unwrap! (map-get? pledges {campaign-id:campaign-id,backer:tx-sender}) err-not-found)))
(asserts! (or (is-eq (get status campaign) status-cancelled) (and (>= block-height (get deadline campaign)) (< pledged-amount (get goal campaign)))) err-forbidden)
(try! (as-contract (stx-transfer? (get amount backer-pledge) tx-sender tx-sender)))
(map-delete pledges {campaign-id:campaign-id,backer:tx-sender})
(ok true)))
(define-public (cancel-campaign (campaign-id uint))
(let ((campaign (unwrap! (map-get? campaigns campaign-id) err-not-found)))
(asserts! (is-eq (get owner campaign) tx-sender) err-forbidden)
(asserts! (is-eq (get status campaign) status-active) err-campaign-cancelled)
(asserts! (not (get claimed campaign)) err-already-claimed)
(map-set campaigns campaign-id (merge campaign {status:status-cancelled}))
(ok true)))
(define-read-only (get-last-campaign-id) (var-get next-campaign-id))
(define-read-only (get-campaign (campaign-id uint)) (map-get? campaigns campaign-id))
(define-read-only (get-pledge (campaign-id uint) (backer principal)) (map-get? pledges {campaign-id:campaign-id,backer:backer}))