Source Code

;; title: Builder Rewards Contract V3 (Enhanced Fees)
;; version: 3.0.0
;; summary: A Clarity contract for Stacks Builder Challenge with 0.1 STX fees and rewards
;; description: Enhanced version with 0.1 STX check-in fee and 0.1 STX rewards.

;; constants
;;
(define-constant contract-owner tx-sender)
(define-constant err-owner-only (err u100))
(define-constant err-already-claimed (err u101))
(define-constant err-not-found (err u102))
(define-constant err-invalid-amount (err u103))
(define-constant err-contract-inactive (err u104))
(define-constant err-insufficient-fee (err u105))

;; Fee constants (in microSTX)
(define-constant app-fee-check-in u100000)      ;; 0.1 STX per check-in
(define-constant app-fee-claim u100000)         ;; 0.1 STX per claim
(define-constant app-fee-score u100000)         ;; 0.1 STX per score submission
(define-constant reward-amount u100000)         ;; 0.1 STX reward

;; data vars
;;
(define-data-var total-rewards-distributed uint u0)
(define-data-var reward-pool uint u4000000) ;; 4 STX initial pool
(define-data-var contract-active bool true)

;; Fee tracking variables
(define-data-var total-fees-collected uint u0)
(define-data-var total-unique-users uint u0)
(define-data-var total-check-ins uint u0)
(define-data-var total-claims uint u0)

;; data maps
;;
(define-map user-claims principal bool)
(define-map user-scores principal uint)
(define-map daily-check-ins principal (list 365 uint))

;; User activity tracking
(define-map user-activity principal {
  first-interaction: uint,
  total-interactions: uint,
  total-fees-paid: uint
})

;; public functions
;;

;; Daily check-in with 0.1 STX fee
(define-public (daily-check-in)
  (let
    (
      (caller tx-sender)
      (current-day (/ stacks-block-height u144)) ;; Approx 1 day in blocks
      (existing-check-ins (default-to (list) (map-get? daily-check-ins caller)))
    )
    (asserts! (var-get contract-active) err-contract-inactive)
    
    ;; Collect 0.1 STX fee
    (try! (stx-transfer? app-fee-check-in caller (as-contract tx-sender)))
    
    ;; Track user and update stats
    (track-user caller app-fee-check-in)
    (var-set total-fees-collected (+ (var-get total-fees-collected) app-fee-check-in))
    (var-set total-check-ins (+ (var-get total-check-ins) u1))
    
    ;; Log event
    (print {
      event: "daily-check-in",
      user: caller,
      fee: app-fee-check-in,
      day: current-day,
      total-fees: (var-get total-fees-collected)
    })
    
    ;; Add current day to check-ins
    (ok (map-set daily-check-ins caller 
      (unwrap-panic (as-max-len? (append existing-check-ins current-day) u365))))
  )
)

;; Claim 0.1 STX reward with 0.1 STX fee
(define-public (claim-daily-reward)
  (let
    (
      (caller tx-sender)
      (current-height stacks-block-height)
      (has-claimed (default-to false (map-get? user-claims caller)))
    )
    (asserts! (var-get contract-active) err-contract-inactive)
    (asserts! (not has-claimed) err-already-claimed)
    (asserts! (>= (var-get reward-pool) reward-amount) err-invalid-amount)
    
    ;; Collect 0.1 STX fee
    (try! (stx-transfer? app-fee-claim caller (as-contract tx-sender)))
    
    ;; Transfer 0.1 STX reward
    (try! (as-contract (stx-transfer? reward-amount tx-sender caller)))
    
    ;; Track user and update stats
    (track-user caller app-fee-claim)
    (map-set user-claims caller true)
    (var-set total-fees-collected (+ (var-get total-fees-collected) app-fee-claim))
    (var-set total-claims (+ (var-get total-claims) u1))
    (var-set total-rewards-distributed (+ (var-get total-rewards-distributed) reward-amount))
    (var-set reward-pool (- (var-get reward-pool) reward-amount))
    
    ;; Log event
    (print {
      event: "claim-reward",
      user: caller,
      reward: reward-amount,
      fee: app-fee-claim,
      total-fees: (var-get total-fees-collected)
    })
    
    (ok reward-amount)
  )
)

;; Record user score with 0.1 STX fee
(define-public (record-score (score uint))
  (let
    (
      (caller tx-sender)
    )
    (asserts! (> score u0) err-invalid-amount)
    (asserts! (var-get contract-active) err-contract-inactive)
    
    ;; Collect 0.1 STX fee
    (try! (stx-transfer? app-fee-score caller (as-contract tx-sender)))
    
    ;; Track user and update stats
    (track-user caller app-fee-score)
    (map-set user-scores caller score)
    (var-set total-fees-collected (+ (var-get total-fees-collected) app-fee-score))
    
    ;; Log event
    (print {
      event: "record-score",
      user: caller,
      score: score,
      fee: app-fee-score,
      total-fees: (var-get total-fees-collected)
    })
    
    (ok true)
  )
)

;; Owner function to add to reward pool
(define-public (fund-rewards (amount uint))
  (begin
    (asserts! (is-eq tx-sender contract-owner) err-owner-only)
    (try! (stx-transfer? amount tx-sender (as-contract tx-sender)))
    (var-set reward-pool (+ (var-get reward-pool) amount))
    
    (print {event: "fund-rewards", amount: amount, new-pool: (var-get reward-pool)})
    (ok true)
  )
)

;; Owner function to withdraw collected fees
(define-public (withdraw-fees)
  (let
    (
      (fee-amount (var-get total-fees-collected))
    )
    (asserts! (is-eq tx-sender contract-owner) err-owner-only)
    (asserts! (> fee-amount u0) err-invalid-amount)
    
    ;; Transfer fees to owner
    (try! (as-contract (stx-transfer? fee-amount tx-sender contract-owner)))
    
    ;; Reset fee counter
    (var-set total-fees-collected u0)
    
    (print {event: "withdraw-fees", amount: fee-amount, recipient: contract-owner})
    (ok fee-amount)
  )
)

;; Toggle contract active status
(define-public (toggle-contract-status)
  (begin
    (asserts! (is-eq tx-sender contract-owner) err-owner-only)
    (var-set contract-active (not (var-get contract-active)))
    (ok (var-get contract-active))
  )
)

;; read only functions
;;

;; Get total fees collected
(define-read-only (get-total-fees-collected)
  (ok (var-get total-fees-collected))
)

;; Get unique user count
(define-read-only (get-unique-user-count)
  (ok (var-get total-unique-users))
)

;; Get comprehensive fee summary
(define-read-only (get-fee-summary)
  (ok {
    total-fees-collected: (var-get total-fees-collected),
    total-unique-users: (var-get total-unique-users),
    total-check-ins: (var-get total-check-ins),
    total-claims: (var-get total-claims),
    total-rewards-distributed: (var-get total-rewards-distributed),
    current-reward-pool: (var-get reward-pool),
    contract-active: (var-get contract-active),
    check-in-fee: app-fee-check-in,
    claim-fee: app-fee-claim,
    reward-amount: reward-amount
  })
)

;; Get user activity details
(define-read-only (get-user-activity (user principal))
  (ok (map-get? user-activity user))
)

;; Get user score
(define-read-only (get-user-score (user principal))
  (ok (default-to u0 (map-get? user-scores user)))
)

;; Get total rewards distributed
(define-read-only (get-total-rewards)
  (ok (var-get total-rewards-distributed))
)

;; Get reward pool balance
(define-read-only (get-reward-pool)
  (ok (var-get reward-pool))
)

;; Check if user has claimed
(define-read-only (has-user-claimed (user principal))
  (ok (default-to false (map-get? user-claims user)))
)

;; Get user check-in count
(define-read-only (get-check-in-count (user principal))
  (ok (len (default-to (list) (map-get? daily-check-ins user))))
)

;; Get contract status
(define-read-only (is-contract-active)
  (ok (var-get contract-active))
)

;; Get user display info
(define-read-only (get-user-display-info (user principal))
  (ok {
    user: user,
    score: (default-to u0 (map-get? user-scores user)),
    claimed: (default-to false (map-get? user-claims user)),
    check-ins: (len (default-to (list) (map-get? daily-check-ins user))),
    activity: (map-get? user-activity user)
  })
)

;; Get fee rates
(define-read-only (get-fee-rates)
  (ok {
    check-in-fee: app-fee-check-in,
    claim-fee: app-fee-claim,
    score-fee: app-fee-score,
    reward-amount: reward-amount
  })
)

;; private functions
;;

;; Track user activity and fees
(define-private (track-user (user principal) (fee-paid uint))
  (match (map-get? user-activity user)
    ;; Existing user - update stats
    activity (map-set user-activity user {
      first-interaction: (get first-interaction activity),
      total-interactions: (+ (get total-interactions activity) u1),
      total-fees-paid: (+ (get total-fees-paid activity) fee-paid)
    })
    ;; New user - create record and increment unique user count
    (begin
      (map-set user-activity user {
        first-interaction: stacks-block-height,
        total-interactions: u1,
        total-fees-paid: fee-paid
      })
      (var-set total-unique-users (+ (var-get total-unique-users) u1))
    )
  )
)

Functions (19)

FunctionAccessArgs
daily-check-inpublic
claim-daily-rewardpublic
record-scorepublicscore: uint
fund-rewardspublicamount: uint
withdraw-feespublic
toggle-contract-statuspublic
get-total-fees-collectedread-only
get-unique-user-countread-only
get-fee-summaryread-only
get-user-activityread-onlyuser: principal
get-user-scoreread-onlyuser: principal
get-total-rewardsread-only
get-reward-poolread-only
has-user-claimedread-onlyuser: principal
get-check-in-countread-onlyuser: principal
is-contract-activeread-only
get-user-display-inforead-onlyuser: principal
get-fee-ratesread-only
track-userprivateuser: principal, fee-paid: uint