Source Code

;; Hajj Savings Contract
;; Savings plan for Hajj pilgrimage
;; Halal - facilitating obligatory pilgrimage
;; Clarity 4 compatible

(define-constant CONTRACT-OWNER tx-sender)
(define-constant ERR-NOT-AUTHORIZED (err u401))
(define-constant ERR-NOT-FOUND (err u404))
(define-constant ERR-GOAL-NOT-MET (err u405))

(define-data-var total-savers uint u0)
(define-data-var total-saved uint u0)

(define-map hajj-plans principal {
  goal: uint, saved: uint, target-year: uint, deposits: uint,
  status: (string-ascii 20), started: uint
})
(define-map deposit-log { saver: principal, index: uint } { amount: uint, block: uint })
(define-map deposit-count principal uint)

(define-public (create-plan (goal uint) (target-year uint))
  (begin
    (asserts! (is-none (map-get? hajj-plans tx-sender)) ERR-NOT-AUTHORIZED)
    (map-set hajj-plans tx-sender { goal: goal, saved: u0, target-year: target-year, deposits: u0, status: "saving", started: stacks-block-height })
    (var-set total-savers (+ (var-get total-savers) u1))
    (ok true)))

(define-public (deposit (amount uint))
  (let (
    (plan (unwrap! (map-get? hajj-plans tx-sender) ERR-NOT-FOUND))
    (idx (default-to u0 (map-get? deposit-count tx-sender)))
    (new-saved (+ (get saved plan) amount))
  )
    (try! (stx-transfer? amount tx-sender CONTRACT-OWNER))
    (map-set deposit-log { saver: tx-sender, index: idx } { amount: amount, block: stacks-block-height })
    (map-set deposit-count tx-sender (+ idx u1))
    (map-set hajj-plans tx-sender (merge plan {
      saved: new-saved, deposits: (+ (get deposits plan) u1),
      status: (if (>= new-saved (get goal plan)) "goal-reached" "saving")
    }))
    (var-set total-saved (+ (var-get total-saved) amount))
    (ok new-saved)))

(define-public (withdraw-for-hajj)
  (let ((plan (unwrap! (map-get? hajj-plans tx-sender) ERR-NOT-FOUND)))
    (asserts! (>= (get saved plan) (get goal plan)) ERR-GOAL-NOT-MET)
    (try! (stx-transfer? (get saved plan) CONTRACT-OWNER tx-sender))
    (map-set hajj-plans tx-sender (merge plan { saved: u0, status: "withdrawn" }))
    (ok (get saved plan))))

(define-public (update-goal (new-goal uint))
  (let ((plan (unwrap! (map-get? hajj-plans tx-sender) ERR-NOT-FOUND)))
    (map-set hajj-plans tx-sender (merge plan { goal: new-goal })) (ok new-goal)))

(define-read-only (get-plan (who principal)) (map-get? hajj-plans who))
(define-read-only (get-deposit (saver principal) (index uint)) (map-get? deposit-log { saver: saver, index: index }))
(define-read-only (get-total-savers) (ok (var-get total-savers)))
(define-read-only (get-total-saved) (ok (var-get total-saved)))
(define-read-only (get-progress (who principal))
  (match (map-get? hajj-plans who) p (ok (/ (* (get saved p) u100) (get goal p))) (ok u0)))

Functions (9)

FunctionAccessArgs
create-planpublicgoal: uint, target-year: uint
depositpublicamount: uint
withdraw-for-hajjpublic
update-goalpublicnew-goal: uint
get-planread-onlywho: principal
get-depositread-onlysaver: principal, index: uint
get-total-saversread-only
get-total-savedread-only
get-progressread-onlywho: principal