Source Code

;; StackSusu NFT v7
;; Membership badges and achievements

(define-constant CONTRACT-OWNER tx-sender)

;; Error constants
(define-constant ERR-NOT-AUTHORIZED (err u5000))
(define-constant ERR-NOT-FOUND (err u5001))
(define-constant ERR-ALREADY-MINTED (err u5002))
(define-constant ERR-INVALID-TYPE (err u5003))

;; NFT types
(define-constant TYPE-MEMBER-BADGE u1)
(define-constant TYPE-CIRCLE-COMPLETE u2)
(define-constant TYPE-TOP-CONTRIBUTOR u3)
(define-constant TYPE-EARLY-ADOPTER u4)
(define-constant TYPE-REPUTATION-TIER u5)

;; NFT counter
(define-data-var nft-counter uint u0)

;; NFT data
(define-map nfts
  uint  ;; token-id
  {
    owner: principal,
    nft-type: uint,
    metadata: (string-ascii 200),
    minted-at: uint,
    circle-id: (optional uint)
  }
)

;; Owner's NFTs
(define-map owner-nfts
  principal
  (list 50 uint)
)

;; Track minted badges per type per user
(define-map minted-badges
  { owner: principal, nft-type: uint }
  bool
)


;; ============================================
;; Mint Functions
;; ============================================

(define-public (mint-member-badge (member principal))
  (let
    (
      (token-id (+ (var-get nft-counter) u1))
      (already-minted (default-to false (map-get? minted-badges { owner: member, nft-type: TYPE-MEMBER-BADGE })))
    )
    (asserts! (not already-minted) ERR-ALREADY-MINTED)
    
    ;; Mint NFT
    (map-set nfts token-id {
      owner: member,
      nft-type: TYPE-MEMBER-BADGE,
      metadata: "StackSusu Member Badge",
      minted-at: block-height,
      circle-id: none
    })
    
    ;; Track ownership
    (map-set owner-nfts member 
      (unwrap! (as-max-len? (append (default-to (list) (map-get? owner-nfts member)) token-id) u50) ERR-NOT-AUTHORIZED))
    
    ;; Mark as minted
    (map-set minted-badges { owner: member, nft-type: TYPE-MEMBER-BADGE } true)
    
    (var-set nft-counter token-id)
    (ok token-id)
  )
)

(define-public (mint-circle-completion (member principal) (circle-id uint))
  (let
    (
      (token-id (+ (var-get nft-counter) u1))
    )
    ;; Mint NFT for completing a circle
    (map-set nfts token-id {
      owner: member,
      nft-type: TYPE-CIRCLE-COMPLETE,
      metadata: "Circle Completion Achievement",
      minted-at: block-height,
      circle-id: (some circle-id)
    })
    
    ;; Track ownership
    (map-set owner-nfts member 
      (unwrap! (as-max-len? (append (default-to (list) (map-get? owner-nfts member)) token-id) u50) ERR-NOT-AUTHORIZED))
    
    (var-set nft-counter token-id)
    (ok token-id)
  )
)

(define-public (mint-reputation-badge (member principal) (tier uint))
  (let
    (
      (token-id (+ (var-get nft-counter) u1))
      (tier-name (if (is-eq tier u5) "Legendary"
                  (if (is-eq tier u4) "Expert"
                    (if (is-eq tier u3) "Advanced"
                      (if (is-eq tier u2) "Intermediate" "Beginner")))))
    )
    ;; Mint tier badge
    (map-set nfts token-id {
      owner: member,
      nft-type: TYPE-REPUTATION-TIER,
      metadata: tier-name,
      minted-at: block-height,
      circle-id: none
    })
    
    ;; Track ownership
    (map-set owner-nfts member 
      (unwrap! (as-max-len? (append (default-to (list) (map-get? owner-nfts member)) token-id) u50) ERR-NOT-AUTHORIZED))
    
    (var-set nft-counter token-id)
    (ok token-id)
  )
)


;; ============================================
;; Transfer
;; ============================================

(define-public (transfer (token-id uint) (recipient principal))
  (let
    (
      (nft (unwrap! (map-get? nfts token-id) ERR-NOT-FOUND))
    )
    (asserts! (is-eq tx-sender (get owner nft)) ERR-NOT-AUTHORIZED)
    
    ;; Update owner
    (map-set nfts token-id (merge nft { owner: recipient }))
    
    ;; Update ownership tracking
    (map-set owner-nfts recipient 
      (unwrap! (as-max-len? (append (default-to (list) (map-get? owner-nfts recipient)) token-id) u50) ERR-NOT-AUTHORIZED))
    
    (ok true)
  )
)


;; ============================================
;; Read Functions
;; ============================================

(define-read-only (get-nft (token-id uint))
  (map-get? nfts token-id)
)

(define-read-only (get-owner (token-id uint))
  (match (map-get? nfts token-id)
    nft (ok (some (get owner nft)))
    (ok none)
  )
)

(define-read-only (get-nft-count)
  (var-get nft-counter)
)

(define-read-only (get-owner-nfts (owner principal))
  (default-to (list) (map-get? owner-nfts owner))
)

(define-read-only (has-badge (owner principal) (nft-type uint))
  (default-to false (map-get? minted-badges { owner: owner, nft-type: nft-type }))
)

Functions (9)

FunctionAccessArgs
mint-member-badgepublicmember: principal
mint-circle-completionpublicmember: principal, circle-id: uint
mint-reputation-badgepublicmember: principal, tier: uint
transferpublictoken-id: uint, recipient: principal
get-nftread-onlytoken-id: uint
get-ownerread-onlytoken-id: uint
get-nft-countread-only
get-owner-nftsread-onlyowner: principal
has-badgeread-onlyowner: principal, nft-type: uint