Source Code

(define-constant ERR_ALREADY_CLAIMED u100)
(define-constant ERR_BADGE_ALREADY_MINTED u101)

(define-constant BLOCKS_PER_DAY u144)
(define-constant BADGE_STREAK u7)

(define-map last-claim-day principal uint)
(define-map streak principal uint)
(define-map badge-claimed principal bool)

(define-data-var next-token-id uint u1)
(define-non-fungible-token badge uint)

(define-read-only (get-current-day)
  (/ stacks-block-height BLOCKS_PER_DAY)
)

(define-read-only (get-streak (user principal))
  (default-to u0 (map-get? streak user))
)

(define-read-only (get-last-claim-day (user principal))
  (default-to u0 (map-get? last-claim-day user))
)

(define-read-only (has-badge (user principal))
  (default-to false (map-get? badge-claimed user))
)

(define-private (mint-badge (user principal))
  (let ((already (default-to false (map-get? badge-claimed user))))
    (if already
        (err ERR_BADGE_ALREADY_MINTED)
        (let ((token-id (var-get next-token-id)))
          (var-set next-token-id (+ token-id u1))
          (map-set badge-claimed user true)
          (match (nft-mint? badge token-id user)
            minted (ok (some token-id))
            err-code (err err-code)
          )
        )
    )
  )
)

(define-public (claim)
  (let (
        (current-day (get-current-day))
        (last-day-opt (map-get? last-claim-day tx-sender))
        (current-streak (default-to u0 (map-get? streak tx-sender)))
       )
    (match last-day-opt last-day
      (if (is-eq current-day last-day)
          (err ERR_ALREADY_CLAIMED)
          (let (
                (new-streak (if (is-eq (+ last-day u1) current-day)
                                (+ current-streak u1)
                                u1))
               )
            (map-set last-claim-day tx-sender current-day)
            (map-set streak tx-sender new-streak)
            (let ((badge-result (if (is-eq new-streak BADGE_STREAK)
                                    (mint-badge tx-sender)
                                    (ok none))))
              (match badge-result
                token-id
                (ok {
                  streak: new-streak,
                  day: current-day,
                  badge-minted: (is-some token-id),
                  token-id: token-id
                })
                err-code
                (if (is-eq err-code ERR_BADGE_ALREADY_MINTED)
                    (ok {
                      streak: new-streak,
                      day: current-day,
                      badge-minted: false,
                      token-id: none
                    })
                    (err err-code))
              )
            )
          )
      )
      (let ((new-streak u1))
        (map-set last-claim-day tx-sender current-day)
        (map-set streak tx-sender new-streak)
        (let ((badge-result (if (is-eq new-streak BADGE_STREAK)
                                (mint-badge tx-sender)
                                (ok none))))
          (match badge-result
            token-id
            (ok {
              streak: new-streak,
              day: current-day,
              badge-minted: (is-some token-id),
              token-id: token-id
            })
            err-code
            (if (is-eq err-code ERR_BADGE_ALREADY_MINTED)
                (ok {
                  streak: new-streak,
                  day: current-day,
                  badge-minted: false,
                  token-id: none
                })
                (err err-code))
          )
        )
      )
    )
  )
)

Functions (6)

FunctionAccessArgs
get-current-dayread-only
get-streakread-onlyuser: principal
get-last-claim-dayread-onlyuser: principal
has-badgeread-onlyuser: principal
mint-badgeprivateuser: principal
claimpublic