Source Code

;; Membership Club Contract
;; On-chain membership management
;; Halal - service-based
;; Clarity 4 compatible

(define-constant CONTRACT-OWNER tx-sender)
(define-constant ERR-NOT-AUTHORIZED (err u401))
(define-constant ERR-ALREADY-MEMBER (err u402))
(define-constant ERR-NOT-MEMBER (err u403))
(define-constant ERR-TIER-NOT-FOUND (err u404))

(define-data-var member-count uint u0)
(define-data-var tier-count uint u0)

(define-map tiers uint { name: (string-utf8 50), fee: uint, duration: uint, perks: (string-utf8 200) })
(define-map members principal { tier: uint, joined: uint, expires: uint, referrer: (optional principal) })
(define-map referral-count principal uint)

(define-public (create-tier (name (string-utf8 50)) (fee uint) (duration uint) (perks (string-utf8 200)))
  (let ((id (+ (var-get tier-count) u1)))
    (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-NOT-AUTHORIZED)
    (map-set tiers id { name: name, fee: fee, duration: duration, perks: perks })
    (var-set tier-count id)
    (ok id)))

(define-public (join (tier-id uint) (referrer (optional principal)))
  (let ((tier (unwrap! (map-get? tiers tier-id) ERR-TIER-NOT-FOUND)))
    (asserts! (is-none (map-get? members tx-sender)) ERR-ALREADY-MEMBER)
    (try! (stx-transfer? (get fee tier) tx-sender CONTRACT-OWNER))
    (map-set members tx-sender { tier: tier-id, joined: stacks-block-height, expires: (+ stacks-block-height (get duration tier)), referrer: referrer })
    (match referrer ref (map-set referral-count ref (+ (default-to u0 (map-get? referral-count ref)) u1)) true)
    (var-set member-count (+ (var-get member-count) u1))
    (ok true)))

(define-public (renew-membership (tier-id uint))
  (let (
    (tier (unwrap! (map-get? tiers tier-id) ERR-TIER-NOT-FOUND))
    (member (unwrap! (map-get? members tx-sender) ERR-NOT-MEMBER))
  )
    (try! (stx-transfer? (get fee tier) tx-sender CONTRACT-OWNER))
    (map-set members tx-sender (merge member { tier: tier-id, expires: (+ (get expires member) (get duration tier)) }))
    (ok true)))

(define-public (revoke-member (member principal))
  (begin
    (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-NOT-AUTHORIZED)
    (map-delete members member)
    (var-set member-count (- (var-get member-count) u1))
    (ok true)))

(define-read-only (get-member (who principal)) (map-get? members who))
(define-read-only (get-tier (id uint)) (map-get? tiers id))
(define-read-only (get-member-count) (ok (var-get member-count)))
(define-read-only (get-tier-count) (ok (var-get tier-count)))
(define-read-only (get-referrals (who principal)) (ok (default-to u0 (map-get? referral-count who))))
(define-read-only (is-active-member (who principal))
  (match (map-get? members who) m (ok (>= (get expires m) stacks-block-height)) (ok false)))

Functions (10)

FunctionAccessArgs
create-tierpublicname: (string-utf8 50
joinpublictier-id: uint, referrer: (optional principal
renew-membershippublictier-id: uint
revoke-memberpublicmember: principal
get-memberread-onlywho: principal
get-tierread-onlyid: uint
get-member-countread-only
get-tier-countread-only
get-referralsread-onlywho: principal
is-active-memberread-onlywho: principal